責任連鎖パターンを用いたコードのリファクタリング

責任連鎖パターン

定義

  • 要求送信者と複数の要求処理者を分離するために、各処理者が次の処理者への参照を持つようにして連鎖を形成する。
  • 要求はこの連鎖を伝搬し、適切な処理者が見つかるまで処理され続ける。

利点

  • オブジェクト間の結合度を低下させる。
  • 新しい処理クラスの追加が容易であり、開閉原則に適合。
  • 処理順序の柔軟な変更が可能。
  • 各オブジェクトは次の参照のみを保持するため、複雑な条件分岐を回避。
  • 各クラスの責任範囲が明確になり、単一責任原則に合致。

欠点

  • 要求が連鎖の末端まで到達しても処理されない可能性がある。
  • 長い連鎖はパフォーマンスに影響。
  • 連鎖構築の責任はクライアントにあり、誤った構築によりシステムエラーが発生する可能性がある。

パターン構造

  • 抽象処理者(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);
        // 持続処理など
    }
}

タグ: Java デザインパターン 責任連鎖パターン リファクタリング Spring Framework

8月7日 20:32 投稿