1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GoFデザインパターン】Chain of Responsibility(責任の連鎖)パターンをJavaで理解する

1
Last updated at Posted at 2026-09-21

はじめに

本記事は、GoFデザインパターン解説シリーズの1つです。

今回から振る舞いに関するパターンに入ります。

最初に紹介するのはChain of Responsibility(責任の連鎖)パターンです。

解決したい課題

「1つの要求(リクエスト)を、複数の候補の中から適切な処理者(ハンドラー)に処理させたい」が、「どの処理者が処理すべきか」を、要求を送る側が事前に決め打ちしたくないという場面があります。

例えば、経費申請の承認フローを考えます。

金額が少額なら「係長」が承認し、一定額を超えると「部長」、さらに高額なら「役員」が承認する、というように、金額に応じて承認者が変わるとします。

これをif文で愚直に書くと、承認者の追加・順序変更のたびに、申請処理側のコードを修正する必要が出てきます。

Chain of Responsibilityパターンは、複数の処理者を鎖(チェーン)のようにつなぎ、要求を先頭の処理者から順番に渡していき、処理できる処理者が現れた時点で処理することで、この問題を解決します。

要求を送る側は、どの処理者が実際に処理するかを一切気にする必要がありません。

image.png

クラス構成

  • Handler(ハンドラー): 要求を処理するメソッドと、次のHandlerへの参照を持つ、共通のインターフェース(または抽象クラス)
  • ConcreteHandler(具体的なハンドラー): 実際の処理を行う、具体的なHandlerの実装。自分で処理できない場合は、次のHandlerに要求を渡す

image.png

Javaでの実装例

経費申請の承認フローを例に実装してみます。

Handler:承認者共通の抽象クラス

public abstract class Approver {
    protected Approver next; // 次の承認者への参照(チェーンの次の輪)

    public Approver setNext(Approver next) {
        this.next = next;
        return next; // メソッドチェーンでチェーンを組み立てやすくするため、nextを返す
    }

    public abstract void approve(ExpenseRequest request);
}

申請内容を表すクラス

public class ExpenseRequest {
    private final String title;
    private final int amount;

    public ExpenseRequest(String title, int amount) {
        this.title = title;
        this.amount = amount;
    }

    public String getTitle() {
        return title;
    }

    public int getAmount() {
        return amount;
    }
}

ConcreteHandler:具体的な承認者たち

public class TeamLeadApprover extends Approver {
    @Override
    public void approve(ExpenseRequest request) {
        if (request.getAmount() <= 50_000) {
            System.out.println("[係長] " + request.getTitle() + "(" + request.getAmount() + "円) を承認しました");
        } else if (next != null) {
            System.out.println("[係長] 自分の権限を超えるため、次の承認者へ回します");
            next.approve(request);
        }
    }
}

public class ManagerApprover extends Approver {
    @Override
    public void approve(ExpenseRequest request) {
        if (request.getAmount() <= 300_000) {
            System.out.println("[部長] " + request.getTitle() + "(" + request.getAmount() + "円) を承認しました");
        } else if (next != null) {
            System.out.println("[部長] 自分の権限を超えるため、次の承認者へ回します");
            next.approve(request);
        }
    }
}

public class ExecutiveApprover extends Approver {
    @Override
    public void approve(ExpenseRequest request) {
        // 役員は上限なく承認できる想定
        System.out.println("[役員] " + request.getTitle() + "(" + request.getAmount() + "円) を承認しました");
    }
}

利用側のコード:チェーンの組み立てと実行

public class Main {
    public static void main(String[] args) {
        Approver teamLead = new TeamLeadApprover();
        Approver manager = new ManagerApprover();
        Approver executive = new ExecutiveApprover();

        // チェーンを組み立てる: 係長 → 部長 → 役員
        teamLead.setNext(manager).setNext(executive);

        // 申請する側は、先頭(teamLead)にだけ要求を渡せばよい
        teamLead.approve(new ExpenseRequest("文房具購入", 5_000));
        System.out.println();
        teamLead.approve(new ExpenseRequest("研修費用", 150_000));
        System.out.println();
        teamLead.approve(new ExpenseRequest("新規サーバー導入", 1_000_000));
    }
}

実行結果:

[係長] 文房具購入(5000円) を承認しました

[係長] 自分の権限を超えるため、次の承認者へ回します
[部長] 研修費用(150000円) を承認しました

[係長] 自分の権限を超えるため、次の承認者へ回します
[部長] 自分の権限を超えるため、次の承認者へ回します
[役員] 新規サーバー導入(1000000円) を承認しました

image.png

申請する側のコードは、常にteamLead.approve(request)という同じ呼び出し方をしているだけです。

金額に応じてどの承認者が最終的に処理するかは、チェーンの内部でバケツリレーのように決まっていきます。

承認者の追加(例えば「係長」と「部長」の間に「課長」を挟む)や、承認金額の上限変更も、Approverの実装クラスとチェーンの組み立て部分だけを修正すればよく、申請側のコードには一切影響しません。

使用場面

  • 複数の候補者(処理者)の中から、条件に応じて実際に処理する者を動的に決めたい場合
  • 承認フロー、例外処理のハンドラーチェーン、イベントのバブリング(GUIにおけるクリックイベントの伝播)など
  • ミドルウェア・フィルターチェーン(Webフレームワークにおけるリクエストの前処理・認証・ログ記録などを、複数の処理を順番に通していく仕組み)
  • 「要求を処理する責任者」を、要求を送る側と処理する側で疎結合に保ちたい場合

まとめ

項目 内容
解決する課題 要求を処理すべき相手を、要求を送る側が事前に決め打ちしたくない
実現方法 複数の処理者を鎖状につなぎ、処理できる処理者が現れるまで順番に要求を渡していく
メリット 要求を送る側は、実際にどの処理者が処理するかを意識しなくてよい
典型例 承認フロー、Webミドルウェアのフィルターチェーン、GUIのイベント伝播

おすすめ書籍

GoFの23パターンをより深く学びたい方には、以下の書籍がおすすめです。
Java言語のサンプルコードとUMLを使い、初心者にもわかりやすく解説されています。

Java言語で学ぶデザインパターン入門 第3版 [ 結城 浩 ]

※本リンクはアフィリエイトリンクを含みます。

参考

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?