ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 职责链模式 (Chainof Responsibility) 责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。 这种模式有另一种称呼:控制链模式。它主要由一系列对于某些命令的处理器构成,每个查询会在处理器构成的责任链中传递,在每个交汇点由处理器判断是否需要对它们进行响应与处理。每次的处理程序会在有处理器处理这些请求时暂停。 在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。 **优点** 1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。 2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。 3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。 4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 **if** 或者 **if···else** 语句。 5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。 **缺点** 1. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。 2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。 3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。 ## **模式的应用场景** 1. 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。 2. 可动态指定一组对象处理请求,或添加新的处理者。 3. 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。 #### **模式的结构** 1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。 2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。 3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。 ![](https://img.kancloud.cn/98/3d/983d6eb1e917592ceab6241661cc915c_812x784.png) ``` <pre class="calibre10">``` <span class="token">//抽象处理者角色</span> abstract class <span class="token4">Handler</span> <span class="token3">{</span> private $next<span class="token3">;</span> <span class="token">//Handler</span> public <span class="token5">function</span> <span class="token4">setNext</span><span class="token3">(</span>Handler $next<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>next <span class="token1">=</span> $next<span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * @return Handler [description] */</span> public <span class="token5">function</span> <span class="token4">getNext</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>next<span class="token3">;</span> <span class="token3">}</span> <span class="token">//处理请求的方法</span> abstract public <span class="token5">function</span> <span class="token4">handleRequest</span><span class="token3">(</span>String $request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//具体处理者角色1</span> class <span class="token4">ConcreteHandler1</span> extends <span class="token4">Handler</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">handleRequest</span><span class="token3">(</span>String $request<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$request <span class="token1">===</span> <span class="token2">"one"</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体处理者1负责处理该请求!"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span><span class="token4">getNext</span><span class="token3">(</span><span class="token3">)</span> <span class="token1">!=</span> <span class="token5">null</span><span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">getNext</span><span class="token3">(</span><span class="token3">)</span><span class="token1">-</span><span class="token1">></span><span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"没有人处理该请求!"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//具体处理者角色2</span> class <span class="token4">ConcreteHandler2</span> extends <span class="token4">Handler</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">handleRequest</span><span class="token3">(</span>String $request<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$request <span class="token1">===</span> <span class="token2">"two"</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体处理者2负责处理该请求!"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span><span class="token4">getNext</span><span class="token3">(</span><span class="token3">)</span> <span class="token1">!=</span> <span class="token5">null</span><span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">getNext</span><span class="token3">(</span><span class="token3">)</span><span class="token1">-</span><span class="token1">></span><span class="token4">handleRequest</span><span class="token3">(</span>request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"没有人处理该请求!"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">ChainOfResponsibilityPattern</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token">//组装责任链</span> $handler1 <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteHandler1</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $handler2 <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteHandler2</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $handler1<span class="token1">-</span><span class="token1">></span><span class="token4">setNext</span><span class="token3">(</span>$handler2<span class="token3">)</span><span class="token3">;</span> <span class="token">//提交请求</span> $handler1<span class="token1">-</span><span class="token1">></span><span class="token4">handleRequest</span><span class="token3">(</span><span class="token2">"two"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> ChainOfResponsibilityPattern<span class="token3">:</span><span class="token3">:</span><span class="token4">main</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> ``` ``` 例子: ``` <pre class="calibre10">``` <span class="token">/** * 职责链模式 Chain of Responsibility * */</span> <span class="token5">function</span> <span class="token4">output</span><span class="token3">(</span>$string<span class="token3">)</span> <span class="token3">{</span> echo $string <span class="token3">.</span> <span class="token2">"n"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * 假如在公司里,如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。   如果0.5<=请假天数<=3天,需要先leader打声招呼,然后部门经理签字。   如果3<请假天数,需要先leader打声招呼,然后到部门经理签字,最后总经经理确认签字, 如果请假天数超过10天,是任何人都不能批准的。 */</span> <span class="token">/** * 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选) * */</span> abstract class <span class="token4">Handler</span> <span class="token3">{</span> protected $_handler <span class="token1">=</span> <span class="token5">null</span><span class="token3">;</span> protected $_handlerName <span class="token1">=</span> <span class="token5">null</span><span class="token3">;</span> <span class="token">/** * 设置下一层级 * @param [type] $handler [description] */</span> public <span class="token5">function</span> <span class="token4">setSuccessor</span><span class="token3">(</span>$handler<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_handler <span class="token1">=</span> $handler<span class="token3">;</span> <span class="token3">}</span> protected <span class="token5">function</span> <span class="token4">_success</span><span class="token3">(</span>$request<span class="token3">)</span> <span class="token3">{</span> <span class="token4">output</span><span class="token3">(</span><span class="token4">sprintf</span><span class="token3">(</span><span class="token2">"%s's request was passed"</span><span class="token3">,</span> $request<span class="token1">-</span><span class="token1">></span><span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token5">return</span> <span class="token6">true</span><span class="token3">;</span> <span class="token3">}</span> abstract public <span class="token5">function</span> <span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * 具体处理者角色1(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。 * leader * */</span> class <span class="token4">ConcreteHandlerLeader</span> extends <span class="token4">Handler</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$handlerName<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_handlerName <span class="token1">=</span> $handlerName<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$request<span class="token1">-</span><span class="token1">></span><span class="token4">getDay</span><span class="token3">(</span><span class="token3">)</span> <span class="token1"><</span> <span class="token6">0.5</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">output</span><span class="token3">(</span><span class="token4">sprintf</span><span class="token3">(</span><span class="token2">'%s was told'</span><span class="token3">,</span> $this<span class="token1">-</span><span class="token1">></span>_handlerName<span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token">// 已经跟leader招呼了</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">_success</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">if</span> <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_handler <span class="token5">instanceof</span> <span class="token4">Handler</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_handler<span class="token1">-</span><span class="token1">></span><span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">/** * 具体处理者角色2 * Manager 部门经理 * */</span> class <span class="token4">ConcreteHandlerManager</span> extends <span class="token4">Handler</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$handlerName<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_handlerName <span class="token1">=</span> $handlerName<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span><span class="token6">0.5</span> <span class="token1"><=</span> $request<span class="token1">-</span><span class="token1">></span><span class="token4">getDay</span><span class="token3">(</span><span class="token3">)</span> <span class="token1">&&</span> $request<span class="token1">-</span><span class="token1">></span><span class="token4">getDay</span><span class="token3">(</span><span class="token3">)</span> <span class="token1"><=</span> <span class="token6">3</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">output</span><span class="token3">(</span><span class="token4">sprintf</span><span class="token3">(</span><span class="token2">'%s signed'</span><span class="token3">,</span> $this<span class="token1">-</span><span class="token1">></span>_handlerName<span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token">// 部门经理签字</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">_success</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">if</span> <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_handler <span class="token5">instanceof</span> <span class="token4">Handler</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_handler<span class="token1">-</span><span class="token1">></span><span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">/** * 具体处理者角色3 * 总经理 */</span> class <span class="token4">ConcreteHandlerGeneralManager</span> extends <span class="token4">Handler</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$handlerName<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_handlerName <span class="token1">=</span> $handlerName<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span><span class="token6">3</span> <span class="token1"><</span> $request<span class="token1">-</span><span class="token1">></span><span class="token4">getDay</span><span class="token3">(</span><span class="token3">)</span> <span class="token1">&&</span> $request<span class="token1">-</span><span class="token1">></span><span class="token4">getDay</span><span class="token3">(</span><span class="token3">)</span> <span class="token1"><</span> <span class="token6">10</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">output</span><span class="token3">(</span><span class="token4">sprintf</span><span class="token3">(</span><span class="token2">'%s signed'</span><span class="token3">,</span> $this<span class="token1">-</span><span class="token1">></span>_handlerName<span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token">// 总经理签字</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">_success</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">if</span> <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_handler <span class="token5">instanceof</span> <span class="token4">Handler</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_handler<span class="token1">-</span><span class="token1">></span><span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> <span class="token4">output</span><span class="token3">(</span><span class="token4">sprintf</span><span class="token3">(</span><span class="token2">'no one can approve request more than 10 days'</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">/** * 请假申请 * */</span> class <span class="token4">Request</span> <span class="token3">{</span> private $_name<span class="token3">;</span> private $_day<span class="token3">;</span> private $_reason<span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$name <span class="token1">=</span> <span class="token2">''</span><span class="token3">,</span> $day <span class="token1">=</span> <span class="token6">0</span><span class="token3">,</span> $reason <span class="token1">=</span> <span class="token2">''</span><span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_name <span class="token1">=</span> $name<span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span>_day <span class="token1">=</span> $day<span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span>_reason <span class="token1">=</span> $reason<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">setName</span><span class="token3">(</span>$name<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_name <span class="token1">=</span> $name<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_name<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">setDay</span><span class="token3">(</span>$day<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_day <span class="token1">=</span> $day<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getDay</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_day<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">setReason</span><span class="token3">(</span>$reason<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_reason <span class="token1">=</span> $reason<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getReason</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_reason<span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">Client</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">test</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> $leader <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteHandlerLeader</span><span class="token3">(</span><span class="token2">'leader'</span><span class="token3">)</span><span class="token3">;</span> $manager <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteHandlerManager</span><span class="token3">(</span><span class="token2">'manager'</span><span class="token3">)</span><span class="token3">;</span> $generalManager <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteHandlerGeneralManager</span><span class="token3">(</span><span class="token2">'generalManager'</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//请求实例</span> $request <span class="token1">=</span> <span class="token5">new</span> <span class="token4">Request</span><span class="token3">(</span><span class="token2">'ucai'</span><span class="token3">,</span> <span class="token6">4</span><span class="token3">,</span> <span class="token2">'休息'</span><span class="token3">)</span><span class="token3">;</span> $leader<span class="token1">-</span><span class="token1">></span><span class="token4">setSuccessor</span><span class="token3">(</span>$manager<span class="token3">)</span><span class="token3">;</span> <span class="token">//设置下一层级</span> $manager<span class="token1">-</span><span class="token1">></span><span class="token4">setSuccessor</span><span class="token3">(</span>$generalManager<span class="token3">)</span><span class="token3">;</span> <span class="token">//设置下一层级</span> <span class="token">//执行请求</span> $result <span class="token1">=</span> $leader<span class="token1">-</span><span class="token1">></span><span class="token4">handleRequest</span><span class="token3">(</span>$request<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> Client<span class="token3">:</span><span class="token3">:</span><span class="token4">test</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> ``` ``` ## **模式的扩展** 职责链模式存在以下两种情况。 1. 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。 2. 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。