07 Dec 2014, 15:33

Java で Chain of Responsibility Pattern を 末尾再帰で実装した

はじめに

Chain of Responsibility Pattern という, マイナーな Gof のパターンがある.

本をよんでみて, これって再帰関数を利用すればもっとシンプルにかけるん じゃないかとおもって, 試してみた.

Chain of Responsibility Pattern

責務を持たせたオブジェクトの Chain に 要求を渡していく.

要求は,

  • そのオブジェクトで処理できればそこで処理する
  • そのオブジェクトで処理できなければ, 次のオブジェクトに渡す.

パターン未適用

[sourcecode language=”java” title=””]
import java.util.List;
import java.util.LinkedList;

public class ChainOfResponsibilitySample {
public static void main (String[] args) {
List chain = new LinkedList();
chain.add (new A ());
chain.add (new B ());
chain.add (new C ());

for (Handler handler : chain) {
if (handler.isMatch (‘b’)) {
handler.execute ();
break;
}
}
}
}

abstract class Handler {
public abstract boolean isMatch (char c);
public abstract void execute ();
}

class A extends Handler {
public boolean isMatch (char c) { return c == ‘a’; }
public void execute () { System.out.println ("a hit"); }
}

class B extends Handler {
public boolean isMatch (char c) { return c == ‘b’; }
public void execute () { System.out.println ("b hit"); }
}

class C extends Handler {
public boolean isMatch (char c) { return c == ‘c’; }
public void execute () { System.out.println ("c hit"); }
}
[/sourcecode]

絶望ポイント

<div class="outline-text-3" id="text-unnumbered-4">
  <p>
    ここがきたない.
  </p>

  <p>
    制御側からいちいち判定用メソッドを読んだり, マッチしたらアクションを起動している.これが面倒.
  </p>

  <p>
    [sourcecode language=&#8221;java&#8221; title=&#8221;&#8221;]<br /> for (Handler handler : chain) {<br /> if (handler.isMatch (&#8216;b&#8217;)) {<br /> handler.execute ();<br /> break;<br /> }<br /> }<br /> [/sourcecode]
  </p>

  <p>
    できれば, ひとつメソッドをよんだら, あとは好き勝手に処理されればいい.
  </p>

  <p>
    Amazon で本を注文するときは, ポチったら, あとはコンビニに勝手に届いて入ればいい.
  </p>
</div>

パターン適用

メリット

<div class="outline-text-3" id="text-unnumbered-6">
  <p>
    要求を出す側と, 要求を処理する側の結びつきが弱まる.
  </p>

  <p>
    具体的にいえば, ループを回さなくてすむ.
  </p>
</div>

コード

<div class="outline-text-3" id="text-unnumbered-7">
  [sourcecode language=&#8221;java&#8221; title=&#8221;&#8221;]<br /> public class ChainOfResponsibilitySample {<br /> public static void main (String[] args) {<br /> Handler chain = new A (new B (new C (null)));<br /> chain.handle (&#8216;b&#8217;);<br /> }<br /> }</p> 

  <p>
    abstract class Handler {<br /> private Handler next;
  </p>

  <p>
    public Handler (Handler next) {<br /> this.next = next;<br /> }
  </p>

  <p>
    public void handle (char c) {<br /> if (isMatch (c))<br /> execute ();<br /> else<br /> next.handle (c);<br /> }
  </p>

  <p>
    abstract boolean isMatch (char c);<br /> abstract void execute ();<br /> }
  </p>

  <p>
    class A extends Handler {<br /> public A (Handler next){ super (next); }<br /> boolean isMatch (char c) { return c == &#8216;a&#8217;; }<br /> void execute () { System.out.println ("a hit"); }<br /> }
  </p>

  <p>
    class B extends Handler {<br /> public B (Handler next){ super (next); }<br /> boolean isMatch (char c) { return c == &#8216;b&#8217;; }<br /> void execute () { System.out.println ("b hit"); }<br /> }
  </p>

  <p>
    class C extends Handler {<br /> public C (Handler next){ super (next); }<br /> boolean isMatch (char c) { return c == &#8216;c&#8217;; }<br /> void execute () { System.out.println ("c hit"); }<br /> }<br /> [/sourcecode]
  </p>
</div>

感動のポイント

<div class="outline-text-3" id="text-unnumbered-8">
  <p>
    みよ! このシンプルさを.
  </p>

  <p>
    [sourcecode language=&#8221;java&#8221; title=&#8221;&#8221;]<br /> public static void main (String[] args) {<br /> Handler chain = new A (new B (new C (null)));<br /> chain.handle (&#8216;b&#8217;);<br /> }<br /> [/sourcecode]
  </p>
</div>

こんなの, 関数型の考え方でかけば当たり前だ!

この主張をしたいがために, この記事を書いた.

関数型っぽくかけば, こんなの当たり前の方法.

[sourcecode language=”java” title=””]
public class ChainOfResponsibilityFinctional {
public static void main (String[] args) {

LinkedList chain = new LinkedList();
chain.add (new A ());
chain.add (new B ());
chain.add (new C ());

handle (chain, ‘b’);
}

static void handle (LinkedList chain, char c) {
Handler head = chain.element ();
chain.removeFirst ();
LinkedList tail = chain;
if (head == null)
return;
else {
if (head.isMatch (c)) {
head.execute ();
return;
}
else
handle (tail, c);
}
}
}

abstract class Handler {
public abstract boolean isMatch (char c);
public abstract void execute ();
}

class A extends Handler {
public boolean isMatch (char c) { return c == ‘a’; }
public void execute () { System.out.println ("a hit"); }
}

class B extends Handler {
public boolean isMatch (char c) { return c == ‘b’; }
public void execute () { System.out.println ("b hit"); }
}

class C extends Handler {
public boolean isMatch (char c) { return c == ‘c’; }
public void execute () { System.out.println ("c hit"); }
}
[/sourcecode]

感動のポイント

<div class="outline-text-3" id="text-unnumbered-10">
  <p>
    一行で一応処理できている.
  </p>

  <p>
    [sourcecode language=&#8221;java&#8221; title=&#8221;&#8221;]<br /> handle (chain, &#8216;b&#8217;);<br /> [/sourcecode]
  </p>

  <p>
    末尾再帰を利用している. しかし, あんまりシンプルにかけないな&#x2026;
  </p>

  <p>
    [sourcecode language=&#8221;java&#8221; title=&#8221;&#8221;]<br /> static void handle (LinkedList<Handler> chain, char c) {<br /> Handler head = chain.element ();<br /> chain.removeFirst ();<br /> LinkedList<Handler> tail = chain;<br /> if (head == null)<br /> return;<br /> else {<br /> if (head.isMatch (c)) {<br /> head.execute ();<br /> return;<br /> }<br /> else<br /> handle (tail, c);<br /> }<br /> }<br /> [/sourcecode]
  </p>

  <p>
    ただし, 呼び元で Handler に対してメッセージをおくっているところはかわらないか.
  </p>

  <p>
    Chain of responsibility は, chain のリスト構造のなかに, 責務をカプセル化している.
  </p>
</div>

おわりに

デコレータパターンやコンポジットパターンでも感じるが, Gof のデザインパターンは, 関数型で書いたほうが便利なことを がんばって OOP で書いているように思えるのだが.

関数型デザインパターン

<div class="outline-text-3" id="text-unnumbered-12">
  <p>
    ネットで調べたら, やはり同じことを考えている人はいるようだ.
  </p>

  <ul class="org-ul">
    <li>
      <a href="http://sujitpal.blogspot.jp/2013/06/functional-chain-of-responsibility.html">Salmon Run: Functional Chain of Responsibility implementation in Scala</a>
    </li>
  </ul>

  <p>
    以下の二つは関数型パラダイムでのデザインパターンにもなりえると思う.
  </p>

  <ul class="org-ul">
    <li>
      Decorator Pattern
    </li>
    <li>
      Chain of Responsibility Pattern
    </li>
  </ul>
</div>