デザインパターン学習メモ:「Chain of Responsibility」


このパターンの目的

GoF本より引用する。

1つ以上のオブジェクトに要求を処理する機会を与えることにより、要求を送信するオブジェクトと受信するオブジェクトの結合を避ける。受信する複数のオブジェクトをチェーン状につなぎ、あるオブジェクトがその要求を処理するまで、そのチェーンに沿って要求を渡していく。

利用者から要求が出されたたときに、あらかじめ定めた一連の担当者たちが順に処理を試みる。

このパターンを使うことでクラス間の結合度が低くなる。
処理を担当する各クラスは、その処理方法と後続のオブジェクトを知っていればよく、全体像については知らなくてもよい。

また、担当者のチェーンは実行時に決定することができるので、柔軟である。

実装例

GoF本と『Java言語で学ぶ デザインパターン入門』を参考に作成。

Support.java
// Handler役
// 処理を担当するクラス(抽象クラス)
public abstract class Support {
    private Support successor; // 次に処理を試みる後続のオブジェクト

    public Support() {
    }

    public void setSuccessor(Support support) {
        this.successor = support;
    }

    public void resolve(EventType type) {
        if (successor != null) {
            successor.resolve(type);
        } else {
            System.out.println("NOBODY RESOLVED");
        }
    }
}
FooSupport.java
// ConcreteHandler役
// 処理を担当するクラス(Handler役を継承)
public class FooSupport extends Support {
    private static final EventType SUPPORT_TYPE = EventType.FOO;

    public FooSupport() {
        super();
    }

    @Override
    public void resolve(EventType type) {
        if (type == SUPPORT_TYPE) {
            System.out.println("RESOLVED by FooSupport");
        } else {
            super.resolve(type);
        }
    }
}
BarSupport.java
// ConcreteHandler役
// 処理を担当するクラス(Handler役を継承)
public class BarSupport extends Support {
    private static final EventType SUPPORT_TYPE = EventType.BAR;

    public BarSupport() {
        super();
    }

    @Override
    public void resolve(EventType type) {
        if (type == SUPPORT_TYPE) {
            System.out.println("RESOLVED by BarSupport");
        } else {
            super.resolve(type);
        }
    }
}
EventType.java
// 要求のタイプ
// Handlerに処理を依頼するときに用いられる
public enum EventType {
    FOO, BAR, BAZ
}

実行例

Main.java
public class Main {
    public static void main(String[] args) {
        Support fooSupport = new FooSupport();
        fooSupport.setSuccessor(new BarSupport());

        fooSupport.resolve(EventType.FOO);
        fooSupport.resolve(EventType.BAR);
        fooSupport.resolve(EventType.BAZ);
    }
}
Main
RESOLVED by FooSupport
RESOLVED by BarSupport
NOBODY RESOLVED

その他の実装例

『Java言語で学ぶ デザインパターン入門』にでてきた例がおもしろかった。

処理の要求(メソッドの呼び出し)の際に、数値を含むオブジェクト(Troubleクラス)を渡す。

処理の担当者(担当クラス)にはLimitSupportクラス、OddSupportクラスなどがあり、前者であれば「ある値以下」、後者であれば「奇数番号」の数値ををもつTroubleを処理する。

処理のチェーンの流れの中で、自分が担当できるToubleを処理し、そうでなければ後続のHandlerに依頼する。

これを例えば1~1000までの数値でTourbleをつくり、処理を要求してみると、要求がたらい回しにされている様子がわかり、このパターンの流れをイメージしやすい。

参考文献

  • エリック ガンマ、ラルフ ジョンソン、リチャード ヘルム、ジョン プリシディース(1999)『オブジェクト指向における再利用のためのデザインパターン 改訂版』本位田 真一、吉田 和樹 監訳、SBクリエイティブ
  • 結城浩(2004)『Java言語で学ぶ デザインパターン入門』SBクリエイティブ