パンくず
書籍 Patterns to Patterns
Creation
Encapsulate Composit Builder
概要
Encapsulate Composit Builderのリファクタリングについて
使用する場面
複雑化したCompositeを含む生成ロジックをクライアントから切り離したい場合
対応方法
Builderを作成し、複雑化した生成ロジックをBuilderでシンプルにする。
利点と欠点
利点
・Compositeの構造のクライアントコードをシンプルにします
・繰り返しが多くエラーの温床になるCompositeの生成を減らす
・Compositeとクライアントを疎結合にする
・複雑なオブジェクトやカプセル化されたCompositeの難しい表現を可能にする
欠点
・インターフェースの目的が明らかではないかもしれない
手順
1.Builderを作成する。結果を返すMethodを一つ追加する。
2.子を追加するMethodを任意の数分作成する。
3.もし、Composite構造のコードなら属性と値を設定出来るようにMethodを追加する
4.クライアントが使いやすいようにシンプルにします
サンプル
リファクタリング前
# encoding: Windows-31J require "pp" =begin 鈴木は田中の師匠 田中は佐藤の師匠 佐藤は阿部の師匠 鈴木は鬼瓦の師匠 =end class Client def set_apprenticeship suzuki = Apprenticeship.new('suzuki') tanaka = Apprenticeship.new('tanaka') sato = Apprenticeship.new('sato') tanaka.add_mentee(sato) suzuki.add_mentee(tanaka) onigawara = Apprenticeship.new('onigawara') suzuki.add_mentee(onigawara) abe = Apprenticeship.new('abe') sato.add_mentee(abe) pp suzuki end end # 徒弟クラス。弟子を持つことが出来る class Apprenticeship attr_accessor:mentees,:name def initialize(name) @name = name; @mentees = Hash.new end def add_mentee(mentee) @mentees.store(mentee.name,mentee) end end Client.new.set_apprenticeship
リファクタリング前出力
#<Apprenticeship:0x2a56430 @mentees= {"tanaka"=> #<Apprenticeship:0x2a56220 @mentees= {"sato"=> #<Apprenticeship:0x2a55ff8 @mentees= {"abe"=>#<Apprenticeship:0x2a55cb0 @mentees={}, @name="abe">}, @name="sato">}, @name="tanaka">, "onigawara"=>#<Apprenticeship:0x2a55ea8 @mentees={}, @name="onigawara">}, @name="suzuki">
リファクタリング後
# encoding: Windows-31J require "pp" =begin 鈴木は田中の師匠 田中は佐藤の師匠 佐藤は阿部の師匠 鈴木は鬼瓦の師匠 =end class Client def set_apprenticeship suzuki = ApprenticeshipBuilder.new("suzuki") suzuki.add_mentee("tanaka") suzuki.add_mentee("onigawara") suzuki.add_mentee_to_parent("tanaka", "sato") suzuki.add_mentee_to_parent("sato", "abe") pp suzuki end end # 徒弟クラス。各人は師匠と弟子を持つことが出来る class Apprenticeship attr_accessor:mentees,:name def initialize(name) @name = name; @mentees = Hash.new end def add_mentee(mentee) @mentees.store(mentee.name,mentee) end end class ApprenticeshipBuilder attr_accessor:apprenticeship def initialize(name) @apprenticeship=Apprenticeship.new(name) end def add_mentee(name) @apprenticeship.add_mentee(Apprenticeship.new(name)) end def add_mentee_to_parent(parent, name) add_mentee_to_parent_apprenticeship(@apprenticeship, parent, name) end # 親に含まれない場合は子のハッシュを再帰で走査する def add_mentee_to_parent_apprenticeship(apprenticeship, parent, name) if apprenticeship.mentees.include?(parent) apprenticeship.mentees[parent].mentees[name] = Apprenticeship.new(name) else apprenticeship.mentees.each do |key, value| add_mentee_to_parent_apprenticeship(apprenticeship.mentees[key], parent, name) end end end private:add_mentee_to_parent_apprenticeship end Client.new.set_apprenticeship
リファクタリング後出力
#<ApprenticeshipBuilder:0x29b34b8 @apprenticeship= #<Apprenticeship:0x29b34a0 @mentees= {"tanaka"=> #<Apprenticeship:0x29b3458 @mentees= {"sato"=> #<Apprenticeship:0x29b3380 @mentees= {"abe"=>#<Apprenticeship:0x29b32f0 @mentees={}, @name="abe">}, @name="sato">}, @name="tanaka">, "onigawara"=>#<Apprenticeship:0x29b33f8 @mentees={}, @name="onigawara">}, @name="suzuki">>