パンくず
書籍 Patterns to Patterns
Creation
Move Creation Knowledge to Factory
概要
Move Creation Knowledge to Factoryのリファクタリングについて
使用する場面
オブジェクトの生成にいくつか設定が必要であり、
生成のための処理を各クライアント側が行なっているような場面。
手順
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)}