Tbpgr Blog

Organization Development Engineer tbpgr(てぃーびー) のブログ

書籍 Refactoring to Patterns | Simplification | Replace Conditional Logic with Strategy

パンくず

書籍 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