Javaにおける責任連鎖パターン:柔軟な処理フローの構築

責任連鎖(Chain of Responsibility)パターンは、リクエストを処理する複数のオブジェクトを連鎖させ、リクエストをそれらのオブジェクトに沿って渡していくデザインパターンです。これにより、リクエストの送信者と受信者が分離され、どのハンドラーがリクエストを処理するかを事前に知る必要がなくなります。各ハンドラーは、自身がリクエストを処理できるか判断し、処理できなければ連鎖内の次のハンドラーにリクエストを渡します。このパターンは、複数のステップからなる処理や、条件に応じて異なる処理が適用されるワークフローに特に適しています。

例えば、システムにおける監視設定の登録プロセスを考えてみましょう。このプロセスには、入力値の検証、関連する操作フローの生成、そして最終的なデータベースへの保存など、複数の段階的な処理が含まれる場合があります。各ステップでエラーが発生した場合、それまでの処理を適切にロールバックするメカニズムも求められます。

以下に、このような監視設定処理フローを責任連鎖パターンで実装する例を示します。

監視設定データモデル

まず、処理対象となる監視設定のデータ構造を定義します。

/**
 * 監視設定のデータモデルを表すクラスです。
 */
public class MonitoringSetting {

    private String identifier; // 設定の一意な識別子
    private String category;   // 設定のカテゴリ
    private String triggerValue; // 閾値などのトリガー値

    public String getIdentifier() {
        return identifier;
    }

    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getTriggerValue() {
        return triggerValue;
    }

    public void setTriggerValue(String triggerValue) {
        this.triggerValue = triggerValue;
    }
}

抽象処理ユニット

次に、責任連鎖を構成する各処理ユニットの抽象クラスを定義します。このクラスは、次のユニットへの参照を持ち、処理ロジックとロールバックロジックの両方を定義します。

/**
 * 責任連鎖の各処理ステップを定義する抽象クラスです。
 * 各ステップは、処理と、失敗時のロールバック機能を提供します。
 */
public abstract class AbstractProcessingUnit {

    // 前の処理ユニットへの参照
    private AbstractProcessingUnit previousLink;
    // 次の処理ユニットへの参照
    private AbstractProcessingUnit nextLink;

    /**
     * 処理ロジックを実行し、成功した場合は次のユニットにリクエストを渡します。
     * 失敗した場合は、ロールバック処理を開始します。
     *
     * @param setting 処理対象の監視設定
     */
    public void executeProcessing(MonitoringSetting setting) {
        try {
            // 各ユニット固有の処理を実行
            performProcessingLogic(setting);
        } catch (Exception e) {
            System.err.println("処理中にエラーが発生しました: " + e.getMessage());
            // エラーが発生した場合、ロールバック処理を開始
            initiateRollback(setting);
            return;
        }

        // 処理が成功した場合、次のユニットがあれば処理を継続
        if (this.nextLink != null) {
            nextLink.executeProcessing(setting);
        }
    }

    /**
     * ロールバック処理を開始します。
     * 現在のユニットのロールバックが完了したら、前のユニットにロールバックを伝播します。
     *
     * @param setting ロールバック対象の監視設定
     */
    public void initiateRollback(MonitoringSetting setting) {
        // 各ユニット固有のロールバック処理を実行
        performRollbackLogic(setting);
        // 現在のユニットのロールバックが完了したら、前のユニットにロールバックを伝播
        if (this.previousLink != null) {
            previousLink.initiateRollback(setting);
        }
    }

    /**
     * 各コンクリート処理ユニットが実装すべき実際の処理ロジックです。
     *
     * @param setting 処理対象の監視設定
     * @throws Exception 処理中にエラーが発生した場合
     */
    public abstract void performProcessingLogic(MonitoringSetting setting) throws Exception;

    /**
     * 各コンクリート処理ユニットが実装すべき実際のロールバックロジックです。
     *
     * @param setting ロールバック対象の監視設定
     */
    public abstract void performRollbackLogic(MonitoringSetting setting);

    /**
     * 次の処理ユニットを設定します。
     * 設定と同時に、次のユニットの前のユニットとして自身を設定します。
     *
     * @param nextLink 次の処理ユニット
     * @return 設定された次の処理ユニット
     */
    public AbstractProcessingUnit setNextLink(AbstractProcessingUnit nextLink) {
        this.nextLink = nextLink;
        nextLink.setPreviousLink(this); // 双方向リンクを設定
        return nextLink;
    }

    /**
     * 前の処理ユニットを設定します。
     * (プライベートメソッドとして、setNextLinkからのみ呼び出されることを想定)
     *
     * @param previousLink 前の処理ユニット
     * @return 設定された前の処理ユニット
     */
    private AbstractProcessingUnit setPreviousLink(AbstractProcessingUnit previousLink) {
        this.previousLink = previousLink;
        return previousLink;
    }
}

具体的な処理ユニットの実装

様々な処理ステップに対応する具体的なハンドラーを実装します。

1. 基準値検証ハンドラー

設定されたトリガー値が有効であるか検証します。

/**
 * 監視設定のトリガー値(閾値)を検証する処理ユニットです。
 */
public class CriterionValidator extends AbstractProcessingUnit {

    @Override
    public void performProcessingLogic(MonitoringSetting setting) throws Exception {
        if (setting.getTriggerValue() == null || setting.getTriggerValue().trim().isEmpty()) {
            throw new Exception("トリガー値が未設定です。検証に失敗しました。");
        }
        System.out.println("【基準検証器】: トリガー値 '" + setting.getTriggerValue() + "' の検証が成功しました。");
    }

    @Override
    public void performRollbackLogic(MonitoringSetting setting) {
        System.out.println("【基準検証器】: トリガー値検証のロールバック処理を実行しました。");
    }
}

2. 操作フロー生成ハンドラー

監視設定に基づいて、システム内で実行される操作フローを生成します。

/**
 * 監視設定に基づき、関連する操作フローを生成する処理ユニットです。
 */
public class OperationFlowGenerator extends AbstractProcessingUnit {

    @Override
    public void performProcessingLogic(MonitoringSetting setting) throws Exception {
        // 実際にはここで複雑なフロー生成ロジックが実行される
        System.out.println("【操作フロー生成器】: 監視設定 '" + setting.getIdentifier() + "' の操作フローが正常に生成されました。");
    }

    @Override
    public void performRollbackLogic(MonitoringSetting setting) {
        System.out.println("【操作フロー生成器】: 操作フロー生成のロールバック処理を実行しました。");
    }
}

3. 設定永続化ハンドラー

検証され、フローが生成された監視設定を永続化(データベースに保存など)します。

/**
 * 監視設定を永続化(保存)する処理ユニットです。
 */
public class SettingStorageHandler extends AbstractProcessingUnit {

    @Override
    public void performProcessingLogic(MonitoringSetting setting) throws Exception {
        if (setting.getIdentifier() == null || setting.getIdentifier().trim().isEmpty()) {
            throw new Exception("設定識別子が空です。永続化に失敗しました。");
        }
        // 実際にはここでデータベースへの書き込みなどが行われる
        System.out.println("【設定永続化ハンドラー】: 設定 '" + setting.getIdentifier() + "' が正常に保存されました。");
    }

    @Override
    public void performRollbackLogic(MonitoringSetting setting) {
        System.out.println("【設定永続化ハンドラー】: 設定永続化のロールバック処理を実行しました。");
    }
}

処理ワークフローの構築と実行

最後に、これらの処理ユニットを連結して責任連鎖を構築し、リクエストを開始するクライアントクラスを作成します。

/**
 * 監視設定の処理ワークフローを構築し、実行するクラスです。
 */
public class ProcessingWorkflow {

    private AbstractProcessingUnit firstStep;

    /**
     * コンストラクタで責任連鎖を初期化します。
     */
    public ProcessingWorkflow() {
        // 連鎖の構築
        firstStep = new CriterionValidator(); // 最初のステップ
        firstStep.setNextLink(new OperationFlowGenerator()) // 次のステップ
                 .setNextLink(new SettingStorageHandler());  // さらに次のステップ
    }

    /**
     * 監視設定の処理を開始します。
     *
     * @param setting 処理対象の監視設定
     */
    public void processSetting(MonitoringSetting setting) {
        System.out.println("\n--- 監視設定処理開始: " + setting.getIdentifier() + " ---");
        firstStep.executeProcessing(setting);
        System.out.println("--- 監視設定処理完了 ---");
    }

    public static void main(String[] args) {
        ProcessingWorkflow workflow = new ProcessingWorkflow();

        // 成功ケース
        MonitoringSetting successSetting = new MonitoringSetting();
        successSetting.setIdentifier("CPU_USAGE_ALERT");
        successSetting.setCategory("Performance");
        successSetting.setTriggerValue("cpuRate > 90");
        workflow.processSetting(successSetting);

        // ロールバックケース1:設定識別子(永続化ステップ)が空の場合
        MonitoringSetting rollbackSetting1 = new MonitoringSetting();
        rollbackSetting1.setIdentifier(""); // 意図的に空にする
        rollbackSetting1.setCategory("Network");
        rollbackSetting1.setTriggerValue("packetLoss > 5%");
        workflow.processSetting(rollbackSetting1);

        // ロールバックケース2:トリガー値(検証ステップ)が空の場合
        MonitoringSetting rollbackSetting2 = new MonitoringSetting();
        rollbackSetting2.setIdentifier("MEMORY_LEAK_DETECTION");
        rollbackSetting2.setCategory("Resource");
        rollbackSetting2.setTriggerValue(""); // 意図的に空にする
        workflow.processSetting(rollbackSetting2);
    }
}

この実装では、監視設定の処理が、検証、フロー生成、永続化という一連のステップを経て行われます。もし途中のステップで問題が発生した場合、自動的にそれまでの処理が連鎖的にロールバックされ、システムの状態が整合性を保つように設計されています。これは、複数の相互依存する処理ステップを持つ複雑なビジネスロジックにおいて、堅牢性と柔軟性を提供する強力なアプローチとなります。

タグ: Java design-patterns chain-of-responsibility error-handling rollback

9月20日 11:55 投稿