デザインパターン_行動パターン - 「責任の連鎖パターン」
ダークホースプログラマによるJavaデザインパターンの詳細解説、23のJavaデザインパターン(図表+フレームワークソースコード解析+実戦)をノートとしてまとめています。
概要
実際には、リクエストは複数のオブジェクトで処理できるが、各オブジェクトの処理条件や権限が異なる、というケースがよくあります。例えば、会社員が休暇を申請した場合、休暇を承認できるリーダーは部長、副部長、部長などですが、各リーダーが承認できる日数は異なります。リーダーは休暇を申請したい日数に応じて署名する必要があり、従業員は各リーダーの名前、電話番号、住所などの情報を覚えておく必要があり、難易度が高くなります。他にも、指導者に出張費の補てんを求めることや、日常生活の中で「太鼓をたたいて花を渡す」ゲームなど、同様の例は数多くある。
意味
- 責任チェーン モード (責任チェーン モードとも呼ばれます) は、リクエスト送信者と複数のリクエスト プロセッサの結合を避けるために、前のオブジェクトから次のオブジェクトの参照を記憶することによって、すべてのリクエスト プロセッサがチェーンに接続されます。 、オブジェクトが処理するまで、リクエストはこのチェーンに沿って渡されます。
構造
責任連鎖パターンには主に次の役割が含まれます。
- 抽象ハンドラー (Handler) ロール: 抽象処理メソッドと後続の接続 (次のオブジェクトへの参照) を含む、リクエストを処理するためのインターフェースを定義します。
- Concrete Handler (コンクリート ハンドラー) の役割: 抽象ハンドラーの処理メソッドを実装し、リクエストが処理可能かどうかを判断し、リクエストを処理できる場合は処理し、そうでない場合はリクエストを後続者に転送します。
- クライアントクラス(Client)の役割: 処理チェーンを作成し、その先頭の特定のプロセッサオブジェクトにリクエストを送信しますが、リクエストの処理内容や配送プロセスは問われません。
事例の実現
ここで、休暇プロセス制御システムを開発する必要があります。1 日未満の休暇の場合はチームリーダーの承認が必要ですが、1 ~ 3 日の休暇の場合は部門長の承認が必要で、3 ~ 7 日の休暇の場合は部長の承認が必要です。
クラス図は次のとおりです。
コードは以下のように表示されます:
-
書面による休暇申請
public class LeaveRequest { private String name; // 姓名 private int num; // 请假天数 private String content; // 请假内容 public LeaveRequest(String name, int num, String content) { this.name = name; this.num = num; this.content = content; } public String getName() { return name; } public int getNum() { return num; } public String getContent() { return content; } }
-
抽象ハンドラーの役割
public abstract class Handler { protected final static int NUM_ONE = 1; protected final static int NUM_THREE = 3; protected final static int NUM_SEVEN = 7; // 该领导处理的请假天数区间 private int numStart; private int numEnd; // 领导上面还有领导(后继连接,下一个对象的引用) private Handler nextHandler; // 设置请假天数范围 上不封顶 public Handler(int numStart) { this.numStart = numStart; } // 设置请假天数范围 public Handler(int numStart, int numEnd) { this.numStart = numStart; this.numEnd = numEnd; } // 设置上级领导 public void setNextHandler(Handler nextHandler){ this.nextHandler = nextHandler; } // 提交请假条 public final void submit(LeaveRequest leave){ if (0 == this.numStart) { return; } // 如果请假天数达到该领导者的处理要求 if (leave.getNum() >= this.numStart) { this.handleLeave(leave); // 如果还有上级 并且请假天数超过了当前领导的处理范围 if (this.nextHandler != null && leave.getNum() > numEnd) { this.nextHandler.submit(leave); // 继续往上提交 责任链模式 } else { System.out.println("流程结束"); } } } // 各级领导处理请假条方法 protected abstract void handleLeave(LeaveRequest leave); }
-
コンクリートプロセッサーの役割
// 具体处理者-小组长 public class GroupLeader extends Handler { public GroupLeader() { // 小组长处理1天以下的请假 super(0, Handler.NUM_ONE); } @Override protected void handleLeave(LeaveRequest leave) { System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。"); System.out.println("小组长审批:同意。"); } } // 具体处理者-部门经理 public class Manager extends Handler { public Manager() { // 部门经理处理1-3天的请假 super(Handler.NUM_ONE, Handler.NUM_THREE); } @Override protected void handleLeave(LeaveRequest leave) { System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。"); System.out.println("部门经理审批:同意。"); } } // 具体处理者-总经理 public class GeneralManager extends Handler { public GeneralManager() { // 部门经理处理3-7天的请假 super(Handler.NUM_THREE, Handler.NUM_SEVEN); } @Override protected void handleLeave(LeaveRequest leave) { System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。"); System.out.println("总经理审批:同意。"); } }
-
テストクラス
public class Client { public static void main(String[] args) { // 请假条来一张 LeaveRequest leave = new LeaveRequest("张三", 5, "身体不适"); // 各位领导 GroupLeader groupLeader = new GroupLeader(); Manager manager = new Manager(); GeneralManager generalManager = new GeneralManager(); groupLeader.setNextHandler(manager); // 小组长的领导是部门经理 manager.setNextHandler(generalManager); // 部门经理的领导是总经理 // 此时责任链已经设置成功了 // 之所以在这里设置上级领导,是因为可以根据实际需求来更改设置,如果实战中上级领导人都是固定的,则可以移到领导实现类中。 // 提交申请 groupLeader.submit(leave); } }
出力
张三请假5天,身体不适。 小组长审批:同意 张三请假5天,身体不适。 部门经理审批:同意 张三请假5天,身体不适。 总经理审批:同意 流程结束!
長所と短所
アドバンテージ
- オブジェクト間の結合の減少
- このパターンにより、リクエストの送信者と受信者の間の結合が軽減されます。
- システムの拡張性の向上
- オープンとクローズの原則を満たすために、必要に応じて新しいリクエスト処理クラスを追加できます。
- オブジェクトへの責任の割り当てにおける柔軟性の向上
- ワークフローが変更されると、チェーン内のメンバーを動的に変更したり、その順序を変更したり、責任を動的に追加または削除したりすることもできます。
- 責任の連鎖により、オブジェクト間の接続が簡素化されます
- オブジェクトは、後続オブジェクトを指す参照を保持するだけでよく、他のすべてのハンドラへの参照を保持する必要がないため、多数の if または if...else ステートメントの使用が回避されます。
- 責任の共有
- 各クラスは、処理すべき作業のみを処理する必要があり、処理されていない作業は次のオブジェクトに渡して完了させ、各クラスの責任範囲を明確にし、クラスの単一責任原則に準拠します。
欠点がある
- すべてのリクエストが処理されるという保証はありません。リクエストには明確な受信者がないため、リクエストが処理されるという保証はなく、リクエストは処理されずにチェーンの下まで到達する可能性があります。
- 責任の長い連鎖と比較して、リクエストの処理には複数の処理オブジェクトが関与する可能性があり、システムのパフォーマンスはある程度の影響を受けます。
- 責任連鎖の確立の合理性はクライアントの保証に依存するため、クライアントの複雑さが増大し、循環呼び出しなどの責任連鎖の誤った設定によりシステム エラーが発生する可能性があります。
ソースコード分析 - JavaWeb
JavaWeb アプリケーション開発において、責任連鎖 (フィルター) パターンの代表的なアプリケーションとして FilterChain が挙げられますが、Filter のシミュレーション実装分析は次のとおりです。
-
Web リクエストのシミュレーション リクエストと Web レスポンス レスポンス
public interface Request { } public interface Response { }
-
アナログウェブフィルター フィルター
public interface Filter { public void doFilter(Request req, Response res, FilterChain c); }
-
具体的なフィルターの実装をシミュレートする
public class FirstFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { System.out.println("过滤器1 前置处理"); // 先执行所有request再倒序执行所有response chain.doFilter(request, response); System.out.println("过滤器1 后置处理"); } } public class SecondFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { System.out.println("过滤器2 前置处理"); // 先执行所有request再倒序执行所有response chain.doFilter(request, response); System.out.println("过滤器2 后置处理"); } }
-
フィルターチェーンの実装をシミュレートする FilterChain
public class FilterChain { private List<Filter> filters = new ArrayList<Filter>(); private int index = 0; // 创建过滤器链对象 -> 链式调用 public FilterChain addFilter(Filter filter) { this.filters.add(filter); return this; } public void doFilter(Request request, Response response) { if (index == filters.size()) { return; } Filter filter = filters.get(index); index++; filter.doFilter(request, response, this); } }
-
テストクラス
public class Client { public static void main(String[] args) { Request req = null; Response res = null; FilterChain filterChain = new FilterChain(); filterChain.addFilter(new FirstFilter()).addFilter(new SecondFilter()); filterChain.doFilter(req, res); } }
出力
过滤器1 前置处理 过滤器2 前置处理 过滤器2 后置处理 过滤器1 后置处理