Tbpgr Blog

元エンジニア 人事 tbpgr(てぃーびー) のブログ

書籍 Refactoring to Patterns | Creation | Replace Constructors with Creation Method

パンくず

書籍 Patterns to Patterns
Creation
Replace Constructors with Creation Method

概要

Replace Constructors with Creation Methodのリファクタリングについて

使用する場面

オーバーロードした多数のコンストラクタが存在し、
クラスを利用する側がどのコンストラクタを利用すればよいのか分かりにくい場面。
このような場合、引数だけでどのコンストラクタを利用するか判断するのは困難であり
紛らわしいコンストラクタが増えるため利用されなくなったコンストラクタなどの発見も
困難になる。

対応方法

インスタンス生成用のメソッドを作成し、適切な名前をつける。
利用されなくなったコンストラクタは削除し、必要だが直接外部から参照されなくなった
コンストラクタはprivateに変更する。

利点と欠点

利点

・どのコンストラクタを利用すればよいか、分かりやすくなる
・同じ引数で別の用途のコンストラクタを持てない、というような通常のコンストラクタの制限にとらわれない
・使われなくなったコンストラクタを発見しやすい
・「Extract Class」や「Extract Subclass」等、さらなるリファクタリングの土台となる

欠点

・標準的なコンスタラクタの生成方法と異なる

手順

1.多数のインスタンスを作り分けるためのコンストラクタを見つける。
コンストラクタの利用側で、生成のロジックに対してpublic staticで「Extract Method」を行う。
こうして作成されたメソッドは「Creation Method」です。
作成したメソッドを生成元のクラス側に「Move Method」で移動します。

2.同種のインスタンスを取得している箇所を全て洗い出して先程作成した「Creation Method」を
参照するように修正します。

3.作成した「Creation Method」が他のコンストラクタを連鎖呼び出ししているだけであれば
メソッドのインライン化によってコンストラクタを削除する。

4.1-3の手順をすべてのコンストラクタに繰り返します。

5.コンストラクタが外部から参照されなくなったらprivete属性に変更します

サンプル

Rubyの場合、そもそもコンストラクタオーバーロード自体が出来ないので
このようなケース自体発生しない。
仮にRubyで目的別にコンストラクタを作成すると以下のようになる。

# encoding: Windows-31J

class ProgrammingLanguage
  attr_accessor:name
  
  def initialize(name)
    @name = name
  end
  
  def self.create_java
    return Java.new
  end
  
  def self.create_ruby
    return Ruby.new
  end
end

class Java < ProgrammingLanguage
  JAVA = "Java"

  def initialize
    @name = JAVA
  end
end

class Ruby < ProgrammingLanguage
  RUBY = "Ruby"

  def initialize
    @name = RUBY
  end
end

languages = [ProgrammingLanguage.create_java,ProgrammingLanguage.create_ruby]
languages.each {|language|puts language.name}

出力結果

Java
Ruby