
Chain of Responsibility(責任の連鎖)というデザインパターンは、
複数の処理オブジェクトを「チェーン(鎖)」のようにつなぎ、順番に処理を委譲していくパターンです。
「会社の承認フロー」や「サポートの問い合わせ対応」などに使えます。
簡単に説明すると、担当者が受け、対応できなければ、上司 → 部長 → 社長へと処理を渡す承認フローのイメージです。
各オブジェクトは「自分で処理するか」「次に渡すか」を判断するのがポイントです。
クライアント(呼び出し側)は「最初のハンドラー」にだけ依頼すれば良くて、柔軟に処理の流れを変更・追加できます。
サンプルコード
承認フローのサンプルコードです。
Handler(抽象クラス)
class Handler {
setNext(handler) {
this.next = handler;
return handler;
}
handle(request) {
if (this.next) {
return this.next.handle(request);
}
console.log("どの担当者も処理できませんでした");
}
}
具体的なハンドラー
class JuniorHandler extends Handler {
handle(request) {
if (request.amount <= 1000) {
console.log(`ジュニアが処理しました:${request.amount}円`);
} else {
console.log("ジュニアでは対応不可 → 次へ");
super.handle(request);
}
}
}
class ManagerHandler extends Handler {
handle(request) {
if (request.amount <= 10000) {
console.log(`マネージャーが処理しました:${request.amount}円`);
} else {
console.log("マネージャーでは対応不可 → 次へ");
super.handle(request);
}
}
}
class CEOHandler extends Handler {
handle(request) {
console.log(`CEOが最終対応:${request.amount}円`);
}
}
チェーンを組む
const junior = new JuniorHandler();
const manager = new ManagerHandler();
const ceo = new CEOHandler();
junior.setNext(manager).setNext(ceo);
// --- 実行 ---
junior.handle({ amount: 500 }); // ジュニアが処理
junior.handle({ amount: 5000 }); // マネージャーが処理
junior.handle({ amount: 50000 }); // CEOが対応
ポイント解説
・Handler:共通インターフェースを定義(次に渡す仕組みもここ)。
・JuniorHandler, ManagerHandler, CEOHandler:各段階の具体的処理。
・setNext() で鎖を形成し、handle() で順に渡す。
数珠繋ぎに、承認をnextで移しているのがポイントですね。
メリット
・条件分岐(if-else)が減り、責任の分離が明確になる。
・新しい処理担当を追加・変更しやすい。
・クライアントは「誰が処理するか」を知らなくていい。
デメリット
・処理の流れを追うのが難しくなる(デバッグしにくい)。
・最後まで誰も処理しないケースに注意。
・過剰にチェーンを作るとパフォーマンスに影響。
よく使われる場面
・ログ出力、例外処理チェーン
・イベント伝播(DOMのバブリングも似た仕組み)
・Webリクエストのミドルウェア処理(Express.jsなど)
・フォームバリデーションの連鎖
おまけサンプルコード
Nodejsの、Express.js風のミドルウェア構造 のコードを作ってみました。
ミドルウェアとは:「リクエスト → 処理 → 次へ渡す」という一連の流れをチェーン(連鎖)でつなぐ仕組みです。
Express.jsでは app.use((req, res, next) => {...}) のように書くと言うのが前提のコードです。
各ミドルウェアは next() を呼ぶと次の処理に進みます。
class App {
constructor() {
this.middlewares = [];
}
// ミドルウェアを登録
use(middleware) {
this.middlewares.push(middleware);
}
// 実行開始
handle(req, res) {
let index = 0;
const next = () => {
const middleware = this.middlewares[index++];
if (middleware) middleware(req, res, next);
};
next(); // 最初のミドルウェアから開始
}
}
// -------------------------------------
const app = new App();
// ログミドルウェア
app.use((req, res, next) => {
console.log('Log:', req.url);
next();
});
// 認証チェックミドルウェア
app.use((req, res, next) => {
if (req.user === 'admin') {
next();
} else {
res.status = 403;
res.body = 'Forbidden';
console.log('認証エラー');
}
});
// 最後の処理
app.use((req, res, next) => {
res.status = 200;
res.body = 'Hello World!';
console.log('レスポンス送信:', res);
});
// -------------------------------------
// 実行例
app.handle({ url: '/home', user: 'admin' }, {});
出力結果
Log: /home
レスポンス送信: { status: 200, body: 'Hello World!' }
ポイント解説
・各ミドルウェアが next() を呼ぶことで「次に進む」。
・呼ばれない場合はチェーンが途中で止まる(認証やエラー処理などに使える)。
・責任の連鎖(Chain of Responsibility)パターンとほぼ同じ考え方。
あとがき
処理するタイプが分かれていて、それぞれの権限が違う場合に使うデザインパターンです。
機能による処理区分(権限)が違う場合などで権限委譲するイメージで使うという事が想像しづらい場合もあるかもしれないので、
類似処理で判定が段階的になっている処理を見つけたら使えるデザインパターンだと理解するとわかりやすいかもしれません。
0 件のコメント:
コメントを投稿