Webアプリ開発エンジニアのための技術情報サイト「テックスコア」

14.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クラスは、第1引数に質問の内容を String インスタンスを、第2引数には、 その難易度を表す Level インスタンスを持つものとし、 Level クラスは、lessThan(Level level)メソッドで、難易度の比較ができるものとします。
	public 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 abstract boolean beAbleToJudge(Question question){
			if(question.level.lessThan(responsibleLevel)){
				return true;
			}
			return false;
		}
		protected void judge(Question question){
			・・・・
		}
	}
この様な設計にすることで、利用者からは、以下のような方法で、「責任の鎖」を自由に決定し、利用することができるようになるでしょう。
	public class Main{
		public void static 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年)

次のページへ TECHSCOREのTOPページへ 次のページへ
techscore(トップページへ)
TECHSCORE書店
TECHSCOREトップページJavaSQLXMLリッチクライアントモデリングセマンティックWebその他技術Tuigwaa