臭い名
Template Methodパターンの継承クラスの重複
臭い英名
Duplicated Template Method Child.
リファクタリング英名
Apply meta programming to remove duplicated code.
改善理由
Template Methodパターンを適用した上で各子クラスの処理が少ない場合
クラスやメソッドの宣言コードばかりになり、類似性が高くなる。
8割〜9割は同じコードを記述することになり、DRY原則に反する。
対応
宣言部のコードの重複を除去するためにメタプログラミングを利用する。
サンプルコード
要件
言語はRubyを使用。
前処理で日付のフォーマット指定、
テンプレートメソッドで任意の日付を設定、
後処理で指定フォーマットの任意の日を標準出力、
というTemplate Methodパターンを適用したクラスを想定します。
リファクタリング前
# encoding: utf-8 require "date" class Base def show format = get_format ret = each_execute output(ret, format) end protected def each_execute raise "not implemented!!" end private def get_format "%Y/%m/%d %H:%M:%S.%L" end def output(ret , format) puts ret.strftime(format) end end class Today < Base def each_execute Date.today end end class Now < Base def each_execute DateTime.now end end Today.new.show Now.new.show
リファクタリング後
# encoding: utf-8 require "date" class Base def show format = get_format ret = each_execute output(ret, format) end protected def each_execute raise "not implemented!!" end private def get_format "%Y/%m/%d %H:%M:%S.%L" end def output(ret , format) puts ret.strftime(format) end end DATES = [Date.today, DateTime.now] classes = [] DATES.each do |date| classes << Class.new(Base) do |klass| define_method(:each_execute) {date} end end CLASSES = [:Today, :Now] CLASSES.each_with_index {|klass, i|eval "#{klass.to_s} = classes[#{i}]"} [Today.new, Now.new].each(&:show)
変更後版に翌日クラスを追加
# encoding: utf-8 require "date" class Base def show format = get_format ret = each_execute output(ret, format) end protected def each_execute raise "not implemented!!" end private def get_format "%Y/%m/%d %H:%M:%S.%L" end def output(ret , format) puts ret.strftime(format) end end DATES = [Date.today, DateTime.now, Date.today + 1] classes = [] DATES.each do |date| classes << Class.new(Base) do |klass| define_method(:each_execute) {date} end end CLASSES = [:Today, :Now, :Tommorow] CLASSES.each_with_index {|klass, i|eval "#{klass.to_s} = classes[#{i}]"} [Today.new, Now.new, Tommorow.new].each(&:show)
出力
2013/09/19 00:00:00.000 2013/09/19 21:51:38.903 2013/09/20 00:00:00.000 # Tommorow版のみ表示
備考
Template Methodパターンを利用していても各子クラスの処理が複雑な場合は、
類似度が下がるのでこのパターンは好ましくない。
またメタプログラミングを利用するため複雑なパターンはさらに複雑になる。
あくまでシンプルで差異が少ない場合に適用した方がよいパターン。