パンくず
書籍 Patterns to Patterns
Simplification
Replace Conditional Logic with Strategy
概要
Replace Conditional Logic with Strategyのリファクタリングについて
使用する場面
Martin Fowler曰く「プログラムの複雑性のもっとも一般的なものの一つが複雑な条件ロジックである。」
複雑な条件分岐がタイプによるものであった場合
対応方法
Strategyを適用し、条件分岐を置き換える
利点と欠点
利点
・条件分岐を削除するか減らすことでアルゴリズムを明白になる
・アルゴリズムのバリエーションを継承にすることでクラスをシンプルにします。
・アルゴリズムを他の実行時にに置き換えるすることを可能にする
欠点
・"Simplify Conditional Expression"の適用が好ましい場合、設計がより複雑になる
・コンテキストクラスとのデータ連携が複雑になる
手順
1.このリファクタリングの最後には真のConcrete Strategyとなるクラスを作ります。
クラスにStrategyとつけることで、コミュニケーションを円滑にします
2.Move Methodで計算処理をStrategyに移動します。
ほとんどのStrategyは計算のためのデータを必要とする。
このデータの扱いには2つのアプローチがあります。
(A)Strategyのコンストラクタにパラメータを渡す
(B)Contextから必要とするデータを渡す
3.Extract ParameterでStrategyのインスタンスにContextを渡す
4.Replace Conditional with PolymorphismをStrategyのメソッドに適用する。
このために、Replace Type Code with Subclassを適用する。
可能ならStrategyを抽象クラスにする。
サンプル
時間に応じて挨拶を使い分けるクラスを作成します
リファクタリング前
# encoding: Windows-31J class Greeting def greet() now = Time.now if is_morning(now) puts "おはようございます" elsif is_afternoon(now) puts "こんにちわ" elsif is_night(now) puts "こんばんは" end end def is_morning(time) return true if time.hour.between?(6,11) return false end def is_afternoon(time) return true if time.hour.between?(12,17) end def is_night(time) return true if (time.hour.between?(18,23)) || (time.hour.between?(0,5)) return false end end greeting = Greeting.new greeting.greet
リファクタリング後
# encoding: Windows-31J class Greeting def greet() GreetingStrategy.new.greet(Time.now) end end class GreetingStrategy def greet(time) if is_morning(time) greeting = MorningGreetingStrategy.new elsif is_afternoon(time) greeting = AfternoonGreetingStrategy.new elsif is_night(time) greeting = NightGreetingStrategy.new end return greeting.greet_each_time end def is_morning(time) time.hour.between?(6,11) ? true : false end def is_afternoon(time) time.hour.between?(12,17) ? true : false end def is_night(time) (time.hour.between?(18,23)) || time.hour.between?(0,5) ? true : false end end class MorningGreetingStrategy < GreetingStrategy def greet_each_time;puts "おはようございます";end end class AfternoonGreetingStrategy < GreetingStrategy def greet_each_time;puts "こんにちわ";end end class NightGreetingStrategy < GreetingStrategy def greet_each_time;puts "こんばんは";end end greeting = Greeting.new greeting.greet