14.Chain of Responsibility パターン
- 2012/04/26 一部修正しました
デザインパターン 14章 Chain of Responsibility パターン
- 14.1 Chain of Responsibility パターンとは
- 14.2 サンプルケース
- 14.3 Chain of Responsibility パターンまとめ
14.1 Chain of Responsibility パターンとは
第14章では Chain of Responsibility パターンを学びます。chain は「鎖」、responsibility は「責任」を意味します。したがって、
Chain of Responsibility は、「責任の鎖」と訳すことができます。Chain of Respoisibility パターンとは、「責任」を負ったものが、「鎖」状につながれた状態をイメージさせるパターンです。
例えば、何かの決済を「課長」にお願いすると、課長決裁で対応できるものであれば、課長が決裁します。しかし、課長決済で対応できないものについては、「部長」に決裁をお願いすることになります。当然、部長が決裁できない内容のものであれば、より上位の決裁責任者に決裁が委ねられます。
Chain of Responsibility パターンは、「責任者」を「鎖状」につないでおき、「いずれかの段階」で、「誰か」が処理をすることを表現するようなパターンです。
14.2 サンプルケース
サンプルケースでは、学校の決裁を考えて見ます。
さて、秋になり、あなたが新人教師として勤める小学校でも秋の遠足が近づいてきました。学級会で、遠足について話し合っていると、お調子者の中川雄介君が
「遠足のおやつはいくらまで?」 と突然質問しました。
中川雄介 | : | 先生、遠足のおやつはいくらまで? |
あなた | : | 300円までです。 |
おやつの金額の制限は、事前に決定されており、あなたが判断することができたので即答しました。では、中川君の質問が、「遠足のおやつにバナナは入る?」という質問や、「親が心配するので、遠足に携帯電話を持って行ってよいですか?」というものであった場合はどうでしょう。これらの事項は、事前に決定されておらず、あなたには決定できない事項です。
こまったあなたは、学年主任のベテラン先生に相談に行きました。
あなた | : | ベテラン先生、遠足のおやつにバナナは入るのでしょうか? |
ベテラン先生 | : | よくある質問ですね、バナナは、おやつではありません。 |
あなた | : | では、親が心配するので、遠足に携帯電話を持っていってよいか?という質問を受けているのですが、これは問題ないでしょうか? |
ベテラン先生 | : |
うーん、これは難しいですね。
これまで、携帯電話なんて話、なかったですからねぇ。一度職員会議で話し合ってみましょう。 |
責任の鎖ができているのがわかるでしょうか?中川君は、自分で判断できないことを、担任のあなたに質問してきた。あなたは、あなたが判断できることは、あなた自身で判断し回答しますが、自分で判断できないことはベテラン先生の判断を仰いぎました。
ベテラン先生は、ベテラン先生自ら判断できることは、自分で判断しますが、自分では判断できないことは職員会議で話し合うようにした。もし、職員会議で決定できないようなことがあれば、校長の鶴の一声に委ねられることもあるでしょうし、もしかしたら、教育委員会に相談しなければならないこともあるでしょう。
「責任を持つものが、自分の裁量で判断できるものに関しては自分で判断し、自分で判断できないものに関しては、次の責任者に判断を任せる」という連鎖が確認できますね。
Chain of Responsibility パターンは、このような「責任」の「鎖」をプログラムで表現するパターンです。Chain of Responsibility
パターンを利用するには、「一般的な責任者を表すクラス」を作成し、それを継承する形で、生徒(中川君)、学級担任(あなた)、学年主任(ベテラン先生)、職員会議などのクラスを作成します。「一般的な責任者を表すクラス」は、「判断」するメソッドと、自分で判断できなかった場合に、判断を仰ぐ、「次の責任者」を表すフィールド変数を持ちます。
各責任者を表すクラスは、この「一般的な責任者を表すクラス」を継承して作成します。
ソースコードで表現すると、以下のようになるでしょうか。
ここでは、Question クラスと、Levelクラスを利用しています。
Questionクラスは、フィールドとして質問の内容を格納するString インスタンスと、質問の難易度を表す Level インスタンスを持つものとします。
Level クラスは、フィールドとして難易度を表すint型の値と、自身の難易度と引数のLevelオブジェクトの難易度を比較するlessThan(Level level)メソッドを持つものとします。
public abstract class Responsible{ private Responsible next = null; private String responsiblePerson = null; public Responsible(String responsiblePerson){ this.responsiblePerson = responsiblePerson; } public Responsible setNext(Responsible next){ this.next = next; return next; } public final void putQuestion(Question question){ if(beAbleToJudge(question)){ judge(question); }else if(next != null){ next.putQuestion(question); }else{ System.out.println("誰にも判断できませんでした。やってみなさい。"); } } protected abstract boolean beAbleToJudge(Question question); protected abstract void judge(Question question); }
public class ClassTeacher extends Responsible{ private Level responsibleLevel = new Level(2); public ClassTeacher(String responsiblePerson){ super(responsiblePerson); } protected boolean beAbleToJudge(Question question){ if(question.level.lessThan(responsibleLevel)){ return true; } return false; } protected void judge(Question question){ ・・・・ } }
この様な設計にすることで、利用者からは、以下のような方法で、「責任の鎖」を自由に決定し、利用することができるようになるでしょう。
public class Main{ public static void main(String args[]){ Responsible nakagawa = new Student("中川雄介"); Responsible rookie = new ClassTeacher("新人先生"); Responsible veteran = new ChiefTeacher("ベテラン先生"); Responsible staffMeeting = new StaffMeeting("職員会議"); nakagawa.setNext(rookie).setNext(veteran).setNext(staffMeeting); nakagawa.judge(new Question("おやつはいくらまで?",new Level(1))); nakagawa.judge(new Question("携帯電話持って行ってよい?",new Level(3))); } }
このように、「責任者」それぞれを、クラスに分けてしまうことで、各責任者の役割を限定することができます。責任者の役割を明確にし、責任者をクラスとして分割することで、より柔軟に、判断の流れである「鎖」を組み替えることができるようになります。
例えば、ある日、ベテラン先生がお休みであれば、ベテラン先生の変わりに、副学年主任である、中堅先生を「鎖」に組み込むことが簡単にできますね。
例えば、ある日、ベテラン先生がお休みであれば、ベテラン先生の変わりに、副学年主任である、中堅先生を「鎖」に組み込むことが簡単にできますね。
一方で、毎回鎖をたどっていくため、処理速度が遅くなることが考えられます。速度重視のプログラムが必要なのか、柔軟性が求められるのか、状況によって判断する必要がありそうです。
14.3 Chain of Responsibility パターンまとめ
Chain of Responsibility パターンの一般的なクラス図は以下のようになります
[引用] 『Java言語で学ぶ デザインパターン入門』(結城浩 ソフトバンクパブリッシング株式会社出版 2001年)