Chain of Responsibilityパターンの例
Chain of Responsibilityパターンの使い方を理解するには、業務の「命令系統」を思い浮かべるとよいでしょう。今、ある要求がJaneの受信ボックスに送られてきたとします。彼女は、要求の子細と自分の権限もしくは能力に基づき、その要求が自分の段階で解決できるものかどうか判断します。自分で対処できないような要求であれば、それを命令系統の上位の人物(課長など)に伝えます。同様に課長も、その要求を自分で解決するか、あるいはさらにその上司へと伝達します。
GoFのデザインパターン本を見ると、「Chain of Responsibilityパターンは、要求を処理できる可能性のあるハンドラが複数存在するような状況に適用される」と説明されています。実際のハンドラについては事前に知らされておらず、誰が要求を処理するかをクライアントが気に掛ける必要はありません。Chain of Responsibilityパターンでは、命令系統を動的に変更することも許されます。
Chain of Responsibilityパターンの考え方を端的に示す例として、Javaの例外処理機構があります。ここでメソッドは別のメソッドからの要求を自分で処理するか、あるいは自分を呼び出したメソッドにそのまま渡して処理してもらいます。
ワークフローを基礎とするシステムもChain of Responsibilityパターンが適用される分野です。ワークフローシステムに共通する典型的な活動の1つに経費の償還があります。
例えば、社員が出張旅費を精算すべく直属の上司に経費報告書(expense report)を提出し、承認を得るケースを考えます。多くの場合、上司は(在席していれば)経費が一定の限度額以内で、他に特別な事情がない限り、経費報告書をその場で承認できます。直属の上司に承認されなかった場合、経費報告書は命令系統の上位にいる次のしかるべき人物へと伝えられます。その人物は副社長かもしれません。あるいは、担当の管理者がたまたま不在で、別の同位の管理者へと伝えられることもあります。副社長の段階で適用される規則や権限は多様であり、思わぬ大物の登場を願うこともあります。いずれにせよ、その時点で経費報告書を手にしている人物は、誰が命令系統の次の人物であるかを承知しています。
リスト1は、ExpenseReport
というクラスに関係するコードです。このクラスでは、金額と、経費が海外旅行(特例)に関係するものかどうかという点と、さらに経費報告書を最終的に処理する(つまり、承認するか拒否する)人物が追跡調査されます。
public enum State { initial, approved, rejected } public class ExpenseReport { private int totalDollarAmount; private boolean isInternationalTravel; private State state = State.initial; private Approver handler; public int getTotalDollarAmount() { return totalDollarAmount; } public void setTotalDollarAmount(int amount) { totalDollarAmount = amount; } public boolean isInternationalTravel() { return isInternationalTravel; } public void setIsInternationalTravel(boolean isInternationalTravel) { this.isInternationalTravel = isInternationalTravel; } public void reject() { state = State.rejected; } public void approve(Approver approver) { this.handler = approver; state = State.approved; } public State state() { return state; } public Approver getHandler() { return handler; } }