概要
rubyでデザインパターンを一通り書いていく。
リポジトリは下記
https://github.com/colorbox/designpattern
Iterator
Iteratorは集合を数え上げる処理を共通化することで、集合を扱いやすくする。
また、集合の情報を隠蔽しすることで、ロジックの変更を行いやすくする。
ソース
class Iterator
def initialize(array)
@array = array
@index = 0
end
def has_next
# compare with index
return (@index + 1) <= @array.count
end
def next
value = @array[@index]
@index = @index + 1
value
end
end
require_relative 'iterator'
iterator = Iterator.new(%w(hoge huga piyo))
while iterator.has_next
puts(iterator.next)
end
Adaptor
既存のクラスの出力やインターフェースを必要な形式に変換する。
実現方法は継承によるものと委譲によるものの、大きく二通り存在する。
このパターンは、既にテストされたクラスをそのまま用いることによって安全性が担保される。
継承による実現
既存クラスのサブクラスを定義し、それをアダプタとして用いる方法。
サブクラスに新しいインターフェースを記述し、インターフェースを通してアダプタパターンを実現する。
委譲による実現
既存クラスのオブジェクトをアダプタオブジェクトに保持させる方法。
アダプタオブジェクトで定義されたメソッド内から必要に応じて既存クラスのメソッドを呼び出して、アダプタパターンを実現する。
継承と委譲について
個人的な所感ですが、既存クラスをそのまま流用して安全性を担保するという側面がある場合は、委譲による実現を行うほうが無難に思えます。
ソース
class Adaptee
def initialize
end
def hoge
"hoge"
end
end
class Adaptor
def initialize
@adaptee = Adaptee.new()
end
def hoge
"<H1>" + @adaptee.hoge + "</H1>"
end
end
require_relative 'adaptor'
adaptor = Adaptor.new()
puts adaptor.hoge
Template Method
処理の抽象化を行う。
処理の大枠を基底クラスで定義し、具体的な処理を各サブクラスに記載する。
事前に処理の大枠が決まっている場合に使用する。
大枠の処理を基底クラスで記述、その際使用されるメソッドはサブクラスでオーバーライドされる事が前提となる。
ソース
class TemplateMethod
def initialize
end
def hoge
hoge1
print("\n")
hoge2
print("\n")
hoge3
print("\n")
end
def hoge1
end
def hoge2
end
def hoge3
end
end
class TemplateMethodSub1 < TemplateMethod
def hoge1
print("--hoge1--")
end
def hoge2
print("--hoge2--")
end
def hoge3
print("--hoge3--")
end
end
class TemplateMethodSub2 < TemplateMethod
def hoge1
print("**hoge1**")
end
def hoge2
print("**hoge2**")
end
def hoge3
print("**hoge3**")
end
end
tm = TemplateMethod.new()
tm.hoge
tm1 = TemplateMethodSub1.new()
tm1.hoge
tm2 = TemplateMethodSub2.new()
tm2.hoge
Factory Method
特定のクラスに対して、そのインスタンスの生成を行うクラスを作成する。
作成するクラスを用意することで、インスタンス作成の前処理や後処理を追加できる。
Factory のインスタンス生成部分はTemplate Methodの応用でもある。
ソース
class Product
def initialize
end
end
class ProductFactory
def initialize
end
def create_product
pre_create
product = create
post_create
return product
end
end
class ConcreteProductA < Product
end
class ConcreteProductAFactory < ProductFactory
def pre_create
print "pre A create\n"
end
def create
print "A create\n"
return ConcreteProductA.new
end
def post_create
print "post A create\n"
end
end
factory = ConcreteProductAFactory.new
factory.create_product
Singleton
インスタンスが一つしかないことを保証するパターン。
コンストラクタをprivateにしておき、クラスオブジェクトとして唯一のインスタンスを定義しておく。
その唯一のインスタンスを取得するメソッドを公開することで、そのクラスのインスタンスが一つしかないことを保証する。
但し、このパターンはグローバル変数的な扱われ方をするため、否定的に見られがち。
ソース
class Singleton
@@singleton = self.new
def self.get_singleton
return @@singleton
end
private
def initialize
end
end
print(Singleton.get_singleton)
print("\n")
print(Singleton.get_singleton)
print("\n")
print(Singleton.get_singleton)
print("\n")
Prototype
生成が複雑なインスタンスをコピーして初期化する手法。
Rubyだとcloneメソッドがあるためそもそもパターンとして自分で実装することはないかも。
ソース
class Prototype
attr_accessor :hoge
@hoge = ""
def initialize(str)
@hoge = str
end
end
pro = Prototype.new("hugahuga")
print pro.hoge
print pro
print("\n")
pro2 = pro.clone
print pro2.hoge
print pro2
Builder
複雑なインスタンスの生成を他のオブジェクト(Director)にまかせる。
また、生成を担当するDirectorクラスはBuilderクラスの公開メソッドを使用してインスタンスの生成を行う。
生成するインスタンスのバリエーションを増やす時は、Builerクラスを増やすことで対応する。
この時、DirectorとBuilderオブジェクトの関係はTemplate Methodと同様になる。
BuilderとTemplate Method
BuilderパターンとTemplate Methodの相違点は、処理の流れを把握しているのがどのクラスか、という点にある。
Template Methodパターンの場合は、処理の流れを把握しているのは基底クラスだが、Builderの場合はDirectorクラスとなる。
参考:
TemplateMEthodとBuilderの違いについて
http://hamasyou.com/blog/2004/06/09/builder/
ソース
class Director
def initialize
end
def construct(builder)
str = "hogehoge"
str = builder.pre_operation(str)
str = builder.operation(str)
str = builder.post_operation(str)
return str
end
end
class LineBuilder
def initialize
end
def pre_operation(str)
"---" + str
end
def operation(str)
"---" + str + "---"
end
def post_operation(str)
str + "---"
end
end
class BraceBuilder
def initialize
end
def pre_operation(str)
"{" + str
end
def operation(str)
"(" + str + ")"
end
def post_operation(str)
str + "}"
end
end
b_builder = BraceBuilder.new
l_builder = LineBuilder.new
print Director.new.construct(b_builder)
print "\n"
print Director.new.construct(l_builder)
Abstract Factory
抽象化されたファクトリ。
FactoryとTemplate Methodを組み合わせたパターン。
ソース
class DinnaerFactory
def initialize
end
def createDinner
return [createAppetizer,createMain]
end
def createMain
end
def createAppetizer
end
end
class Soup
def initialize
end
end
class Rice
def initialize
end
end
class Salad
def initialize
end
end
class Pizza
def initialize
end
end
class JapaneseFactory < DinnaerFactory
def createMain
Rice.new
end
def createAppetizer
Soup.new
end
end
class ItalyFactory < DinnaerFactory
def createMain
Pizza.new
end
def createAppetizer
Salad.new
end
end
japanese_f = JapaneseFactory.new
print japanese_f.createDinner
print "\n"
italy_f = ItalyFactory.new
print italy_f.createDinner
参考
http://futurismo.biz/archives/2805
Bridge
クラスにおける「実装」と「機能」を分割する。
クラスを、自分のメソッドのみで実現可能な「機能」と、それらのメソッドを記述した「実装」とに分割する。
「実装」はクラス内に委譲オブジェクトとして保持しておく。
このオブジェクトを切り替えることで機能と実装を分離し、容易にバリエーションを増やすことが可能。
Template Methodの応用とも取れる。
ソース
class OutputImpleHoge
def initialize
end
def output
"hoge"
end
end
class OutputImpleOptional
@option = ""
def initialize(str)
@option = str
end
def output
@option
end
end
class Output
attr_accessor :impl
@impl = nil
def initialize(impl)
@impl = impl
end
def output
@impl.output
end
def multiple
[@impl.output,@impl.output,@impl.output]
end
end
op = Output.new(OutputImpleHoge.new)
print op.output
print"\n"
print op.multiple
print"\n"
opo = Output.new(OutputImpleOptional.new("piyo"))
print opo.output
print"\n"
print opo.multiple
print"\n"
Strategy
処理を切り替える。
「処理」をオブジェクトとして切り出すことで、その切替を容易にするパターン。
例えば、シューティングゲームの敵の弾パターンや、敵の移動パターンの切り替えに使える。
ソース
class Enemy
attr_accessor :move_pattern, :x, :y
def initialize(move_pattern)
@move_pattern = move_pattern
@x = 0
@y = 0
end
def move
@move_pattern.run(self)
end
end
class SideStraghtPattern
def initialize
end
def run(enemy)
enemy.x = enemy.x+1
end
end
class StraightPattern
def initialize
end
def run(enemy)
enemy.y = enemy.y+1
end
end
enemy = Enemy.new(StraightPattern.new)
print enemy.x.to_s + "," + enemy.y.to_s + "\n"
enemy.move
print enemy.x.to_s + "," + enemy.y.to_s + "\n"
enemy.move
print enemy.x.to_s + "," + enemy.y.to_s + "\n"
enemy = Enemy.new(SideStraghtPattern.new)
print enemy.x.to_s + "," + enemy.y.to_s + "\n"
enemy.move
print enemy.x.to_s + "," + enemy.y.to_s + "\n"
enemy.move
print enemy.x.to_s + "," + enemy.y.to_s + "\n"
Composite
入れ子や木のような再帰構造を実現するパターン。
基底クラスを継承した枝葉クラスを使用して、再帰構造を実現する。
ソース
class Entry
def initialize(name)
@name = name
end
def add
end
def prints
print @name + "\n"
end
end
class CompositeFile < Entry
end
class Directory < Entry
def initialize(name)
super(name)
@entries = []
end
def add(entry)
@entries.push(entry)
end
def prints
super
@entries.each do |entry|
entry.prints
end
end
end
dir1 = Directory.new("dir1")
dir1.add(CompositeFile.new("1"))
dir1.add(CompositeFile.new("2"))
dir1.add(CompositeFile.new("3"))
dir1.prints
Decorator
オブジェクトを保持しておき、その処理に機能追加を行う。
この構造が階層構造になるような場合のパターン。
単なるwrapperを階層的にしたパターンとも言える。
基底クラスを保持して階層構造を実現するという点でCompositeパターンと同じ。
Compositeパターンを内包したパターンとも言える。
ソース
class BaseOutputer
def initialize(str)
@str = str
end
def run
return @str
end
end
class Decorator < BaseOutputer
def initialize(outputer)
@outputer = outputer
end
end
class LineDecorator < Decorator
def run
return "|" + @outputer.run + "|"
end
end
class CurlyBraceDecorator < Decorator
def run
return "{" + @outputer.run + "}"
end
end
ld = LineDecorator.new(BaseOutputer.new("hoge"))
print ld.run + "\n"
cd = CurlyBraceDecorator.new(ld)
print cd.run
Visitor
処理と構造を分割するパターン。
処理クラスと構造クラスを用意する。
このパターンは処理に対して開いており、構造に対して閉じた実装となる。
処理の追加は楽だが、構造を修正すると、それに付随して全ての処理クラスを修正しなくてはならなくなるため、手間が大きい。
参考
http://objectclub.jp/community/memorial/homepage3.nifty.com/masarl/article/dp-ocp-2.html
ソース
class Entry
def initialize(name)
@name = name
end
def add
end
def prints
@name
end
end
class CompositeFile < Entry
def accept(visitor)
visitor.visit(self)
end
end
class Directory < Entry
def initialize(name)
super(name)
@entries = []
end
def add(entry)
@entries.push(entry)
end
def prints
@name
end
def members
@entries
end
def accept(visitor)
visitor.visit(self)
end
end
class Visitor
def initialize
end
def visit(entry)
if entry.class.to_s =="Directory"
visit_directory(entry)
else
visit_file(entry)
end
end
end
class UpcaseVisitor < Visitor
def visit_file(entry)
print entry.prints.upcase + "\n"
end
def visit_directory(entry)
print entry.prints.upcase + "\n"
entry.members.each do |member|
member.accept self
end
end
end
class DowncaseVisitor < Visitor
def visit_file(entry)
print entry.prints.downcase + "\n"
end
def visit_directory(entry)
print entry.prints.downcase + "\n"
entry.members.each do |member|
member.accept self
end
end
end
dir1 = Directory.new("dir1")
dir1.add(CompositeFile.new("a1"))
dir1.add(CompositeFile.new("b2"))
dir1.add(CompositeFile.new("C3"))
up_visitor = UpcaseVisitor.new
dir1.accept up_visitor
down_visitor = DowncaseVisitor.new
dir1.accept down_visitor
Chain of Responsibility
責任のたらい回し。
特定の処理が可能なオブジェクトに行き着くまでその処理をたらい回しにする。
ソース
class Problem
attr_accessor :difficulty
def initialize(difficulty)
@difficulty = difficulty
end
end
class Resolver
attr_accessor :next
def initialize
@next = nil
end
end
class OddResolver < Resolver
def resolve(problem)
if problem.difficulty%2==1
print("resolved by odd\n")
true
else
print("odd cannot resolve\n")
return false unless @next
@next.resolve(problem)
end
end
end
class WeakResolver < Resolver
def resolve(problem)
if problem.difficulty < 10
print("resolved by weak\n")
true
else
print("weak cannot resolve\n")
return false unless @next
@next.resolve(problem)
end
end
end
class StrongResolver < Resolver
def resolve(problem)
if problem.difficulty < 100
print("resolved by strong\n")
true
else
print("strong cannot resolve\n")
return false unless @next
@next.resolve(problem)
end
end
end
odd_r = OddResolver.new
wr = WeakResolver.new
sr = StrongResolver.new
odd_r.next = wr
wr.next = sr
pro1 = Problem.new(11)
odd_r.resolve(pro1)
print"---\n"
pro2 = Problem.new(8)
odd_r.resolve(pro2)
print"---\n"
pro3 = Problem.new(100)
odd_r.resolve(pro3)
print"---\n"
Facade
クラスのAPIを制限するパターン。
特定のクラスのオブジェクトに対して、呼び出せるメソッドを少なくすることで、呼び出しやすくする。
一般的なカプセル化、情報隠蔽とも言える。
Mediator
多くの要素がある際の、ご意見役として機能するオブジェクトを用意する。
複数のクラスが相互に連動して動くような場合、一括で管理及び処理を行うオブジェクトを用意して、そのオブジェクトを中心に動作をおこなわせる。
個々のオブジェクトがやり取りを行うと、カプセル化が壊れてしまうためである。
iOSにおけるUIViewControllerなどがこれにあたる。
ソース
class PartsManager
attr_accessor :door, :switch
def collegue_changed
return if (@switch.on && @door.is_open)||(!@switch.on && !@door.is_open)
if @switch.on
@door.open
else
@door.close if @door.is_open
end
end
end
class Part
attr_accessor :mediator
def initialize(mediator)
@mediator = mediator
end
end
class Door < Part
attr_accessor :is_open
def initialize(mediator)
super(mediator)
@is_open = false
end
def open
print "ドアが空いた\n"
@is_open = true
@mediator.collegue_changed
end
end
class Switch < Part
attr_accessor :on
def initialize(mediator)
super(mediator)
@on = false
end
def push
print "ボタンが押された\n"
@on = true
@mediator.collegue_changed
end
end
pm = PartsManager.new
door = Door.new(pm)
switch = Switch.new(pm)
pm.door = door
pm.switch = switch
print pm.door
print pm.switch
switch.push
Observer
オブジェクトの監視を行うパターン。
監視対象のオブジェクトに変化が発生した時、その変化を他のオブジェクトに通知させたい時に使用する。
GUIなどはその典型例。
Rubyにはこのパターンを実現するためのObservableモジュールが存在する。
ソース
class NumberGenerator
attr_accessor :observer, :number
def initialize(observer)
@observer = observer
@number = 0
end
def update
observer.notify(self)
end
def execute
@number = Random.new.rand(20)
update
end
end
class NumberObserver
def notify(generator)
print(generator.number)
end
end
o = NumberObserver.new
g = NumberGenerator.new(o)
(1..100).to_a.each do
g.execute
print(",")
end
Memento
状態を保存するパターン。
特定タイミングにおけるオブジェクトの状態を保存しておき、それを復元可能とするパターン。
復元可能という点が、単夏履歴とは異なる。
また、このパターンに関連して、「狭いインターフェース」「広いインターフェース」という考え方が存在する。
広いインターフェースと狭いインターフェース
この2つの単語は、元々GoFがこのパターンでのみ補足的に用いている用語である。
状態を保存する際は、保存に必要な情報を全て知っておかなくてはいけない。(wide interface)
また、状態保存を行わない外部のオブジェクトに対しては、適切に情報隠蔽を行う。(narrow interface)
ソース
class Memento
attr_accessor :money
def initialize(money)
@money = money
end
end
class Gamer
attr_accessor :money
def initialize
@money = 0
end
def save
return Memento.new(money)
end
def load(memento)
@money = memento.money
end
end
g = Gamer.new
print(g.money)
print("\n")
g.money = 200
m = g.save
print(g.money)
print("\n")
g.money = 19
print(g.money)
print("\n")
g.load(m)
print(g.money)
print("\n")
State
状態を表現するパターン。
状態クラスを定義し、それらを切り替えることで状態遷移などを実現する。
次にどの状態に遷移するかという情報が各状態クラスの中に記述されている。
状態数が多くなりすぎると状態遷移図などが必要になる。
各状態でやらせる処理が単純な場合、このパターンを適用するのは逆に手間となる。
その場合は単純なenumとswitchで良い。
各状態で行わせる処理が煩雑な場合、switchで処理をさせようとするとコードが非常に長くなる。
そのような場合にのみこのパターンを適用すべきだろう。
ソース
class State
attr_accessor :count, :context
def initialize(context)
@count = 0
@context = context
end
end
class RunState < State
def do_something
print"running\n"
@count = @count+1
if @count > 3
@context.change_state(StopState.new(@context))
end
end
end
class StopState < State
def do_something
print"stoping\n"
@count = @count+1
if @count > 3
@context.change_state(RunState.new(@context))
end
end
end
class Person
attr_accessor :state
def initialize
end
def set_state(state)
@state = state
end
def change_state(state)
@state = state
end
def do_something
@state.do_something
end
end
p = Person.new
p.set_state(StopState.new(p))
(1..10).each do
p.do_something
end
Flyweight
リソースの使い回しを行うためのパターン。
いちいち生成していると時間がかかってしまうような場合に使用する。
キャッシュなどが近い。
ソース
class Flyweight
attr_accessor :pool
def initialize
@pool = {}
end
def get_number(key)
if @pool[key]
print "use cache #{key}\n"
return @pool[key]
end
print "created #{key}\n"
@pool[key] = key
return @pool[key]
end
end
f = Flyweight.new
f.get_number(1)
f.get_number(1)
f.get_number(2)
f.get_number(2)
Proxy
データアクセスにおける中間層を定義する。
中間層に付随的な処理を記述することにより、データアクセスそのものとそれに付随する処理を分離できる。
ソース
class RealObject
attr_accessor :secret_info
def initialize(secret_info)
@secret_info = secret_info
end
end
class Proxy
attr_accessor :real_object
def initialize(real_object)
@real_object = real_object
end
def secret_info
print "warning use secret_infoz\n"
return @real_object.secret_info
end
end
p = Proxy.new(RealObject.new("secret"))
print p.secret_info
Command
命令とそれに紐づく処理をクラスとして切り出すパターン
ソース
class Target
attr_accessor :value
def initialize(value)
@value = value
end
end
class Command
def execute
end
end
class PlusCommnad < Command
attr_accessor :target, :value
def initialize(target,value)
@target = target
@value = value
end
def execute
@target.value = @target.value + @value
end
end
class Multicommand < Command
def initialize(target,value)
@target = target
@value = value
end
attr_accessor :target, :value
def execute
@target.value = @target.value * @value
end
end
class CompositeCommand < Command
attr_accessor :commands
def initialize(commands)
@commands = commands
end
def add_command
@commands.push(command)
end
def execute
@commands.each do |command|
command.execute
end
end
end
t = Target.new(10)
cc = CompositeCommand.new([PlusCommnad.new(t,1),Multicommand.new(t,2)])
print(t.value)
print"\n"
cc.execute
print(t.value)
print"\n"
Interpreter
中間言語を表現するためのpターン、使い所は限られるため割愛。
参考
http://www.techscore.com/tech/DesignPattern/index.html/
http://morizyun.github.io/blog/categories/design-pattern/
http://www002.upp.so-net.ne.jp/ys_oota//mdp/index.htm
http://www.blackwasp.co.uk/gofpatterns.aspx