Java.use(better);

Java.use(better);


Episode#02
Strategy パターンの隘路 -- GoF を反面教師に


《関連記事》

GoF の隘路:クロージャーを活用すると

Strategy パターンを導入すると、開発者(プログラマー)は、利用者(プログラマー)には影響を与えずに、その問題解決に必要な戦略を自由に選択できます。たとえば、文字列を連結するのに、演算子 + を利用する戦略と、バッファー StringBuffer を利用する戦略が考えられます。「情報隠蔽の原則」に従うなら、利用者はその違いを知らず、また知る必要もありません。すると、

    def data = "ABCDEFGH".toList

    val contextA = new Context( data => {
      var s = ""
      data foreach { s += _ }
      s
    })
    println("A: %s" format (contextA strategy data))

    val contextB = new Context( data => {
      val buf = new StringBuffer
      data foreach { buf.append(_) }
      buf.toString
    })
    println("B: %s" format (contextB strategy data))

利用者は、2つの戦略 contextA/contextB の違いを意識せずに、同じメソッド呼び出し strategy data を再利用できます。開発者は、2つの戦略の違いを「クロージャー内に隠蔽」できます。注目に値するのは、利用者/開発者の違いを示すコードの断片がすべて、ここに集約されていることです。他に冗長な仕掛は不要です。

Scala でこれを実現するには、

class Context(val strategy: List[Char] => String)

class を宣言するだけなので、意図的にバグを盛り込む余地がありません。ところが、Java で同じことを実現すると、

class Context {
  private Strategy strategy; 
  Context(Strategy strategy) {
    this.strategy = strategy;
  } 
  String execute(char[] data) {
    return strategy.execute(data);
  }
}

これだけの「冗長なコード」が必要になり、バグの温床になりかねません。

interface Strategy {
  public String execute(char data); 
}
class ConcreteStrategyA implements Strategy {
  public String execute(char data) {
    String s = "";
    for (char c: data) { s += c; }
    return s;
  }
}
class ConcreteStrategyB implements Strategy {
  public String execute(char[] data) {
    StringBuffer buf = new StringBuffer();
    for (char c: data) { buf.append(c); }
    return buf.toString();
  }
}

 ↑ TOP

》作業中です《

update*13/01/08 0:13:24