チェーン・オブ・レスポンシビリティ・パターンは、リクエストを処理する複数のオブジェクトを連結し、各オブジェクトが自身で処理できるかどうかを判断し、処理できない場合はチェーン内の次のオブジェクトにリクエストを渡すというデザインパターンです。このパターンは、リクエストの送信者と受信者を分離し、処理の柔軟性を高めることを目的とします。
このパターンを数学的に解釈すると、集合論の直積と直和、そして線形代数の基底ベクトルの概念を応用してその本質を理解し、より直接的にコードに反映させることができます。
リクエストや結果は、複数の独立した属性(要素)の組み合わせとして捉えることができます。これは数学の直積に相当します。各属性は、その構造体の「基底ベクトル」のようなものと解釈できます。
- リクエストの集合
R
: タイプ、データ、優先度などの属性から構成されます。
- 結果の集合
S
: ステータス、出力データ、エラー情報などの属性から構成されます。
各ハンドラは、リクエストの集合
ハンドラの処理は、入力リクエスト
ハンドラがリクエストを「処理した」か「処理しなかった(次のハンドラに渡す)」という選択肢は、数学の直和の概念に相当します。各ハンドラの出力は、「結果 S」と「未処理(empty)」の2つの排他的な状態のいずれかであると表現できます。
全体としての処理写像
チェーン・オブ・レスポンシビリティ・パターンにおける「責務」とは、リクエストや結果を構成する基底ベクトル(特定の属性)に対する操作として定義できます。
各ハンドラは、
- 入力リクエストの特定の基底ベクトル(例: リクエストタイプ、優先度)を検査し、自身が処理すべきかどうかを判断します。
- 処理する場合は、出力結果の特定の基底ベクトル(例: 処理ステータス、出力データ)を生成または変更します。
これにより、抽象的な「責務」が、データ構造の具体的な属性(基底ベクトル)に対する変換として数学的に定義されます。
チェーン・オブ・レスポンシビリティ・パターンは、オブジェクト指向のクラスベースだけでなく、関数型プログラミングによっても非常に自然に表現できます。特に、ハンドラを「リクエストを受け取り、Optional
な結果を返す関数」として定義し、その関数群を配列(またはリスト)として管理することで、上記集合論的な概念がコード上に直接現れます。
以下にJava(record
と関数型インターフェースを使用)によるコード例を示します。
複数の属性を持つリクエストと結果は、Javaのrecord(あるいは不変なclass)として定義され、これが直積の概念を直接的に表現します。
// リクエストの属性(要素)
class RequestMetadata { /* fields and constructor */ }
class RequestPayload { /* fields and constructor */ }
// リクエスト全体(直積)
class Request {
RequestMetadata metadata;
RequestPayload payload;
// constructor and getters
}
// 結果の属性(要素)
class ProcessStatus { /* fields and constructor */ }
class ProcessOutput { /* fields and constructor */ }
class ProcessError { /* fields and constructor */ }
// 結果全体(直積)
class Result {
String requestId;
ProcessStatus status;
ProcessOutput output; // 成功時のみ
ProcessError error; // 失敗時のみ
// constructor and getters
}
各ハンドラは、Request
を受け取り、Optional<Result>
を返す関数型インターフェースとして定義します。Optional<Result>
が、処理された結果(Result
)または未処理(Optional.empty()
)の直和を表現します。
import java.util.Optional;
import java.util.function.Function;
@FunctionalInterface
interface RequestHandler extends Function<Request, Optional<Result>> {}
ハンドラのリストを順に適用することでチェーンを形成します。最初のハンドラが結果を返したら、その結果が全体の出力となります。
import java.util.List;
import java.util.ArrayList;
public class FunctionalChainOfResponsibility {
private final List<RequestHandler> handlers;
public FunctionalChainOfResponsibility(List<RequestHandler> handlers) {
this.handlers = new ArrayList<>(handlers);
}
public Optional<Result> handle(Request request) {
for (RequestHandler handler : handlers) {
Optional<Result> result = handler.apply(request);
if (result.isPresent()) {
return result; // 処理されたら即座に結果を返す
}
}
return Optional.empty(); // どのハンドラも処理しなかった場合
}
public static void main(String[] args) {
// --- 具体的なハンドラの定義 ---
// 各ハンドラはラムダ式で定義され、リクエストの特定の属性(基底ベクトル)を検査し、
// 適切な結果(出力の基底ベクトル)を生成する「責務」を持つ。
RequestHandler highPriorityHandler = request -> {
if (request.payload().priority() >= 90) {
// 責務: 高優先度リクエストの処理
return Optional.of(new Result(
request.metadata().requestId(),
new ProcessStatus("HIGH_PRIORITY_OK", "緊急処理完了"),
new ProcessOutput("緊急データ", "text"), null
));
}
return Optional.empty(); // 次へ渡す
};
RequestHandler dataProcessingHandler = request -> {
if ("DATA_PROC".equals(request.payload().type())) {
// 責務: DATA_PROCタイプのリクエストの処理
return Optional.of(new Result(
request.metadata().requestId(),
new ProcessStatus("DATA_PROC_OK", "データ処理完了"),
new ProcessOutput("処理済みデータ", "json"), null
));
}
return Optional.empty(); // 次へ渡す
};
RequestHandler defaultFallbackHandler = request -> {
// 責務: 最終的なフォールバック処理
return Optional.of(new Result(
request.metadata().requestId(),
new ProcessStatus("FAILED", "処理失敗"),
null, new ProcessError("NO_HANDLER", "該当ハンドラなし")
));
};
// --- チェーンの構築 ---
List<RequestHandler> handlers = List.of(
highPriorityHandler,
dataProcessingHandler,
defaultFallbackHandler
);
FunctionalChainOfResponsibility chain = new FunctionalChainOfResponsibility(handlers);
// --- リクエストの処理 ---
Request req1 = new Request(new RequestMetadata("ID001", "127.0.0.1", 123L), new RequestPayload("AUTH", 95, "token"));
chain.handle(req1).ifPresent(res -> System.out.println("Result: " + res.status().code())); // HIGH_PRIORITY_OK
Request req2 = new Request(new RequestMetadata("ID002", "192.168.1.1", 456L), new RequestPayload("DATA_PROC", 50, "raw data"));
chain.handle(req2).ifPresent(res -> System.out.println("Result: " + res.status().code())); // DATA_PROC_OK
Request req3 = new Request(new RequestMetadata("ID003", "10.0.0.1", 789L), new RequestPayload("REPORT", 10, "report_id"));
chain.handle(req3).ifPresent(res -> System.out.println("Result: " + res.status().code())); // FAILED
}
}
チェーン・オブ・レスポンシビリティ・パターンは、リクエスト処理の「責務」を、数学的な集合の直積で表現された入力・出力の各属性(基底ベクトル)に対する部分写像として明確に定義し、これらの写像の実行を直和の概念(処理するか、しないか)を用いて順序付けられたハンドラ群(配列)で実現するものです。
関数型プログラミングを用いることで、この数学的なモデルがコード上に直接的に反映され、より簡潔で表現力の高い設計が可能になります。