Double Dispatchパターン

最近オブジェクト指向設計の書籍を読み出したので、自分で理解するためにちょこちょこいろいろと書くことにします。

たとえば「ジャンケンする」というプログラムを組む際に、必要となる概念クラスは、「ぐー」「ちょき」「ぱー」となるわけです。で、当然これらは共通のインターフェースを実装しているはずです(「じゃんけんオブジェクト」と名付けます)。

じゃあ実際にじゃんけんぽんと勝負をする訳ですが、当然2つのじゃんけんオブジェクト通しで勝負させたいですよね。(ifひとつめの引数がぐーでふたつめの引数がちょきならtrueを返す、なんてプログラムを書きたくないわけです)

そうすると、まずベタに考えてみると…、

public class Goo implements JyankenObject {
  public boolean fight( JyankenObject aite ) {
    if( aite instanceof Goo ){
      return false;  // ここではあいこも負けというコトにします 
    } else if( aite instanceof Tyoki ) {
      return true;  // かち
    } else if( aite instanceof Paa ) {
      return false; // まけ
    }
  }
}

となるわけですが、これだとこのGooクラスが引数の情報をいろいろと駆使しないといけないですし、なにより拡張性が低い(じゃんけんで第4の手が出てくることはないですが、もしそうなったときにif文に1行追加する羽目になる)わけです。

で、これをDouble Dispatchパターンで考えてみるとこうなります。

public class Goo implements JyankenObject {
  public boolean fight( JyankenObject aite ) {
    // 引数のオブジェクトに対して「ぐー(つまり自分)に勝てるか」聞いて、その逆を返す
    return !aite.fightGoo();  
  }
  public boolean fightGoo(){
    return false;  // 自分はぐーと勝負すると引き分け
  }
  public boolean fightTyoki(){
    return true;  // 自分はちょきと勝負するとかつ
  }
  public boolean fightPaa(){
    return false;  // 自分はぱーと勝負するとまける
  }
}

つまり引数が渡された側がその引数でいろいろと判断するのではなく、そいつに判断させた結果を受けて、さらに判断させた側がその結果を基に判断するというパターンです(2回判断しているのでDouble)。

つまり引数が渡された側…つまり処理実行結果を返さなければいけないこのぐーオブジェクトは、引数に何で勝負させたら良いかを知っています(自分自身、つまりぐーです)。なのでそれを実行させ、その結果を受けて、「こいつはぐーと勝負結果が勝ちなんだったら、つまり自分が勝負したら負けだな」(その逆もある)という判断を行って返すわけです。
じゃんけんに第4の手が出てきたときは、JyankenObjectインターフェースを実装した第4の手クラスと、既存の3クラスに対して第4の手との勝負結果を返すメソッドを定義すればすむわけです。
これでif文を書く必要が無くなりますね。