Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

書籍 Refactoring to Patterns | Creation | Encapsulate Composit Builder

パンくず

書籍 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.クライアントが使いやすいようにシンプルにします

5.Composite構造のコードをBuilderを呼ぶようにリファクタリングします。

サンプル

リファクタリング
# 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">>