責任連鎖パターン
定義
- 要求送信者と複数の要求処理者を分離するために、各処理者が次の処理者への参照を持つようにして連鎖を形成する。
- 要求はこの連鎖を伝搬し、適切な処理者が見つかるまで処理され続ける。
利点
- オブジェクト間の結合度を低下させる。
- 新しい処理クラスの追加が容易であり、開閉原則に適合。
- 処理順序の柔軟な変更が可能。
- 各オブジェクトは次の参照のみを保持するため、複雑な条件分岐を回避。
- 各クラスの責任範囲が明確になり、単一責任原則に合致。
欠点
- 要求が連鎖の末端まで到達しても処理されない可能性がある。
- 長い連鎖はパフォーマンスに影響。
- 連鎖構築の責任はクライアントにあり、誤った構築によりシステムエラーが発生する可能性がある。
パターン構造
- 抽象処理者(Handler):共通インターフェースを定義。
- 具象処理者(Concrete Handler):具体的な処理を実装。
- クライアント:連鎖を構築し、要求を送信。
実装例
前提条件
物資情報変更時に、現在の物資が特定の業務プロセス内に存在するかを検証する処理。
従来のコード
public class ItemValidator {
public static boolean validate(String itemCode) {
if (purchaseCompleted(itemCode)) return true;
if (receivingCompleted(itemCode)) return true;
return false;
}
}
改善後の設計
共通インターフェース
public interface RequestHandler<T> {
void setNext(RequestHandler<T> handler);
void process(T data);
}
抽象基底クラス
@Slf4j
public abstract class BaseHandler<T> implements RequestHandler<T> {
private RequestHandler<T> next;
@Override
public void setNext(RequestHandler<T> handler) {
this.next = handler;
}
@Override
public void process(T data) {
if (next != null) {
next.process(data);
} else {
log.info("処理完了");
}
}
public static <T> ChainBuilder<T> createChain() {
return new ChainBuilder<>();
}
static class ChainBuilder<T> {
private RequestHandler<T> first;
private RequestHandler<T> last;
public ChainBuilder<T> addHandler(RequestHandler<T> handler) {
if (first == null) {
first = handler;
last = handler;
} else {
last.setNext(handler);
last = handler;
}
return this;
}
public RequestHandler<T> build() {
return first;
}
}
}
ハンドラアノテーション
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface HandlerConfig {
String module() default "DEFAULT";
int priority() default 0;
}
具体的処理クラス
@Component
@Slf4j
@HandlerConfig(module = "ASSET", priority = 1)
public class PurchaseValidationHandler extends BaseHandler<String> {
@Override
public void process(String itemCode) {
log.info("品目コード{}の変更確認中:PurchaseValidationHandler", itemCode);
if (purchaseCompleted(itemCode)) {
throw new IllegalStateException("現在進行中の購買プロセスがあります");
}
super.process(itemCode);
}
}
@Component
@Slf4j
@HandlerConfig(module = "ASSET", priority = 2)
public class ReceivingValidationHandler extends BaseHandler<String> {
@Override
public void process(String itemCode) {
log.info("品目コード{}の変更確認中:ReceivingValidationHandler", itemCode);
if (receivingCompleted(itemCode)) {
throw new IllegalStateException("現在進行中の受領プロセスがあります");
}
super.process(itemCode);
}
}
処理管理クラス
@Component
public class ProcessManager {
@Autowired
private ApplicationContext context;
public void executeProcess(String module, String itemCode) {
Map<String, RequestHandler<String>> handlers = context.getBeansOfType(RequestHandler.class);
List<RequestHandler<String>> filtered = handlers.values().stream()
.filter(h -> h.getClass().isAnnotationPresent(HandlerConfig.class))
.map(h -> new AbstractMap.SimpleEntry<>(h, h.getClass().getAnnotation(HandlerConfig.class)))
.filter(e -> e.getValue().module().equals(module))
.sorted(Comparator.comparingInt(e -> e.getValue().priority()))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
RequestHandler<String> chain = BaseHandler.<String>createChain()
.addHandler(filtered.toArray(new RequestHandler[0]))
.build();
chain.process(itemCode);
}
}
利用例
public class AssetController {
@Autowired
private ProcessManager manager;
public Response updateAsset(String itemCode) {
manager.executeProcess("ASSET", itemCode);
// 持続処理など
}
}