Tbpgr Blog

Recruiting Operations tbpgr(てぃーびー) のブログ

書籍 Refactoring to Patterns | Creation | Move Creation Knowledge to Factory

パンくず

書籍 Patterns to Patterns
Creation
Move Creation Knowledge to Factory

概要

Move Creation Knowledge to Factoryのリファクタリングについて

使用する場面

オブジェクトの生成にいくつか設定が必要であり、
生成のための処理を各クライアント側が行なっているような場面。

対応方法

インスタンス生成の処理をカプセル化するFactoryを導入する。

利点と欠点

利点

・生成のロジックを1箇所に統合する
・生成のロジックをクライアントから分離する

欠点

コンストラクタで生成するよりも設計が複雑になる

手順

1.パラメータの設定が必要となるようなコンストラクタを利用しているクラスを探す。
このクラスが「Creation Method」を利用していなければ作成する。

2.インスタンス生成を行うFactoryクラスを新たに作成します。

3.「Move Method」により「Creation Method」をFactoryクラスに移動します
もし「Creation Method」がstaticならnon-staticに変更します。

4.利用側のクラスの呼び出しを「Creation Method」から「Factory」メソッド
呼ぶように修正します。

サンプル

プログラム言語クラス(ProgrammingLanguage)があり、これを継承したJavaクラス(Java)とRubyクラス(Ruby)があります。
この言語を利用するクラス(Client)では、言語ごとに生成するクラスを切り替える処理を行なっています。
このままだと、似たような処理を行うたびに色々な利用者クラスで同様のロジックが記述されることになります。
また、言語の種類が増えるたびに各クライアントは呼び出しロジックを修正しなければならなくなります。

ここで、Factoryメソッドを導入することで生成のロジックをクライアントから追い出し、
重複を排除し、拡張性を高めます。

リファクタリング
# encoding: Windows-31J

class ProgrammingLanguage
  attr_accessor:name
  def initialize(name);@name = name;end
  def coding();puts "コーディング";end
end

class Java < ProgrammingLanguage
  JAVA = "Java"
  def initialize;@name = JAVA;end
  def coding();puts "Javaでコーディング";end
end

class Ruby < ProgrammingLanguage
  RUBY = "Ruby"
  def initialize;@name = RUBY;end
  def coding();puts "Rubyでコーディング";end
end

class Client
  def use_language(language_name)
    language = nil?
    if language_name == Java::JAVA
      language = Java.new
    elsif language_name == Ruby::RUBY
      language = Ruby.new
    end
    language.coding
  end
end

[Java::JAVA,Ruby::RUBY].each {|language_name|Client.new.use_language(language_name)}
リファクタリング

Factoryメソッドを導入後、さらにDart言語を追加しました。
既存クラスへの影響なく追加出来ました。

# encoding: Windows-31J

class ProgrammingLanguage
  attr_accessor:name
  def initialize(name);@name = name;end
  def coding();puts "コーディング";end
end

class Java < ProgrammingLanguage
  JAVA = "Java"
  def initialize;@name = JAVA;end
  def coding();puts "Javaでコーディング";end
end

class Ruby < ProgrammingLanguage
  RUBY = "Ruby"
  def initialize;@name = RUBY;end
  def coding();puts "Rubyでコーディング";end
end

class Dart < ProgrammingLanguage
  DART = "Dart"
  def initialize;@name = DART;end
  def coding();puts "Dartでコーディング";end
end

class LanguageFactory
  def create_language(language_name)
    language_class = eval "#{language_name}"
    return language_class.new
  end
end

class Client
  @language_factory
  def initialize;@language_factory = LanguageFactory.new;end
  def use_language(language_name);@language_factory.create_language(language_name).coding;end
end

[Java::JAVA,Ruby::RUBY,Dart::DART].each {|language_name|Client.new.use_language(language_name)}

出力結果

リファクタリング

Javaでコーディング
Rubyでコーディング

リファクタリング

Javaでコーディング
Rubyでコーディング
Dartでコーディング