Javaにおける責任連鎖パターン:堅牢なリクエスト処理メカニズムの構築
別名
- コマンドチェーン
- オブジェクトチェーン
- 責任チェーン
責任連鎖デザインパターンの意図
Javaにおける責任連鎖パターンは、複数のオブジェクトにリクエストを処理する機会を与えることで、リクエストの送信者と受信者をデカップリングする振る舞いデザインパターンです。受信オブジェクトはチェーン状に接続され、リクエストはオブジェクトが処理するまでチェーンに沿って渡されます。
現実世界の例を用いた責任連鎖パターンの詳細な説明
現実世界の例
Javaにおける責任連鎖パターンの現実世界の例として、テクニカルサポートのコールセンターがあります。このJavaデザインパターンを実装する場合、各サポートレベルはチェーン内のハンドラを表します。顧客が問題を報告して電話をかけると、まず最前線のサポート担当者が対応します。問題が単純であれば、担当者が直接処理します。問題が複雑であれば、担当者は2段階目のサポート技術者に電話を転送します。このプロセスは、問題を解決できるスペシャリストに到達するまで、複数のサポートレベルでエスカレーションされます。各サポートレベルはチェーン内のハンドラを表し、リクエストは適切なハンドラが見つかるまでチェーンに沿って渡されます。これにより、リクエストと特定の受信者がデカップリングされます。
平易な言葉で説明すると
オブジェクトのチェーンを構築するのに役立ちます。リクエストは一端から入り、適切なハンドラが見つかるまでオブジェクトからオブジェクトへと渡されます。
Wikipediaによると
オブジェクト指向設計において、責任連鎖パターンは、コマンドオブジェクトのソースと一連の処理オブジェクトからなるデザインパターンです。各処理オブジェクトには、処理できるコマンドオブジェクトの種類を定義するロジックが含まれています。それ以外のものは、チェーン内の次の処理オブジェクトに渡されます。
責任連鎖パターンのプログラミング例
このJavaの例では、オークの王が命令を出し、責任連鎖パターンを表すコマンドチェーンによって処理されます。以下のコードスニペットを使用して、Javaでこのデザインパターンを実装する方法を学びましょう。
オークの王は軍隊に大声で命令を出します。最も早く反応するのは司令官、次に将校、そして兵士です。司令官、将校、兵士は責任の連鎖を形成します。
まず、`Request`クラスがあります。
@Getter
public class Request {
private final RequestType requestType;
private final String requestDescription;
private boolean handled;
public Request(final RequestType requestType, final String requestDescription) {
this.requestType = Objects.requireNonNull(requestType);
this.requestDescription = Objects.requireNonNull(requestDescription);
}
public void markHandled() {
this.handled = true;
}
@Override
public String toString() {
return getRequestDescription();
}
}
public enum RequestType {
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
}
次に、`RequestHandler`階層を示します。
public interface RequestHandler {
boolean canHandleRequest(Request req);
int getPriority();
void handle(Request req);
String name();
}
@Slf4j
public class OrcCommander implements RequestHandler {
@Override
public boolean canHandleRequest(Request req) {
return req.getRequestType() == RequestType.DEFEND_CASTLE;
}
@Override
public int getPriority() {
return 2;
}
@Override
public void handle(Request req) {
req.markHandled();
LOGGER.info("{} handling request \"{}\"", name(), req);
}
@Override
public String name() {
return "Orc commander";
}
}
// OrcOfficer and OrcSoldier are defined similarly as OrcCommander ...
`OrcKing`が命令を出し、チェーンを形成します。
public class OrcKing {
private List<RequestHandler> handlers;
public OrcKing() {
buildChain();
}
private void buildChain() {
handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier());
}
public void makeRequest(Request req) {
handlers
.stream()
.sorted(Comparator.comparing(RequestHandler::getPriority))
.filter(handler -> handler.canHandleRequest(req))
.findFirst()
.ifPresent(handler -> handler.handle(req));
}
}
動作する責任連鎖。
public static void main(String[] args) {
var king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner"));
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
}
コンソール出力
Orc commander handling request "defend castle"
Orc officer handling request "torture prisoner"
Orc soldier handling request "collect tax"
責任連鎖パターンのクラス図

Javaで責任連鎖パターンを使用するケース
責任連鎖を使用するケース
- 複数のオブジェクトがリクエストを処理できる場合、かつハンドラが事前に不明な場合。ハンドラは自動的に特定される必要があります。
- 受信者を明示的に指定せずに、複数のオブジェクトのいずれかにリクエストを発行する場合。
- リクエストを処理できるオブジェクトのセットを動的に指定する場合。
Javaにおける責任連鎖パターンの現実世界のアプリケーション
- UIコンポーネント階層の複数のレベルでイベントが処理される可能性のあるGUIフレームワークにおけるイベントバブリング
- リクエストが処理オブジェクトのチェーンを通過するミドルウェアフレームワーク
- メッセージが、それぞれ異なる方法で処理する可能性のある一連のロガーを通過する可能性のあるロギングフレームワーク
- java.util.logging.Logger#log()
- Apache Commons Chain
- javax.servlet.Filter#doFilter()
責任連鎖パターンのメリットとトレードオフ
メリット
- 結合度の低下。リクエストの送信者は、リクエストを処理する具体的なハンドラを知る必要がありません。
- オブジェクトへの責任の割り当ての柔軟性の向上。チェーンのメンバーと順序を変更することで、リクエスト処理の責任を追加または変更できます。
- 具体的なハンドラがリクエストを処理できない場合に、デフォルトのハンドラを設定できます。
トレードオフ
- 特にチェーンが長く複雑な場合、デバッグとフローの理解が困難になる可能性があります。
- チェーンに包括的なハンドラが含まれていない場合、リクエストが未処理のままになる可能性があります。
- 適切なハンドラを見つけるまで、またはまったく見つからない場合、複数のハンドラを経由する必要があるため、パフォーマンスの問題が発生する可能性があります。
関連するJavaデザインパターン
- コマンド:リクエストをオブジェクトとしてカプセル化するために使用でき、チェーンに沿って渡すことができます。
- コンポジット:責任連鎖パターンは、コンポジットパターンと組み合わせて使用されることがよくあります。
- デコレーター:デコレーターは、責任連鎖パターンにおける責任と同様にチェーン状に接続できます。