深入解析职责链模式:纸牌游戏与采购单分级审批的链式处理
“一比二”、“过关”、“过关”……这声音是不是很耳熟?我想到了什么?正确的!卡片。在像“斗地主”这样的纸牌游戏中,有人向他的下一个玩家打出一张牌,下一个玩家看他手中的牌。如果他买不起前一个玩家的卡,他会将卡请求转发给下一个玩家。 ,下一个人就会做出判断。一个循环后,如果没有其他人买得起这张牌,原来的玩家可以打出一张新牌。在这个过程中,卡牌作为请求沿着一条链传递,卡牌的每个玩家都可以处理该请求。在设计模式中,我们还有一个专门设计的模式来处理这种请求的链式传递,这就是本章将要介绍的责任链模式。

采购订单分级审批
软件中如何实现采购订单的分级审批? Sunny Software公司的开发人员提出了初步的解决方案,在系统中提供一个采购订单处理类PurchaseRequestHandler来统一处理采购订单。框架代码如下:
[java]代码
//采购订单处理类
类PurchaseRequestHandler {
//向总监提交采购订单
publicvoid sendRequestToDirector(PurchaseRequest请求){
if (request.getAmount() < 50000) {
//主管可以批准采购订单
this.handleByDirector(请求);
elseif (request.getAmount() < 100000) {
//副董事长可以批准采购订单
this.handleByVicePresident(请求);
elseif (request.getAmount() < 500000) {
//主席可以批准采购订单
this.handleByPresident(请求);
别的 {
//董事会可以批准采购订单
this.handleByCongress(请求);
//主管批准采购订单
publicvoid handleByDirector(PurchaseRequest 请求) {
//代码省略
//副董事长批准采购订单
publicvoid handleByVicePresident(PurchaseRequest 请求){
//代码省略
//主席批准采购订单
publicvoid handleByPresident(PurchaseRequest 请求) {
//代码省略
//董事会批准采购订单
publicvoid handleByCongress(PurchaseRequest 请求) {
//代码省略
问题看似很简单,但仔细分析后发现上述方案存在以下问题:
(1)PurchaseRequestHandler类比较庞大,各级审批方法都集中在一个类中,违反了“单一责任原则”,测试和维护困难。
(2)如果需要增加新的审批级别或调整任意级别的审批金额和审批明细(例如将主席审批金额改为60万元),必须修改源代码并进行严格测试。另外,如果需要去掉某个级别时(例如金额10万元以上的采购订单由董事长直接审批,不再有副董事长职位),源代码也必须是修改,违反了“开闭原则”。
(三)审批流程设置缺乏灵活性。目前的审批流程为“董事-->副董事长-->董事长-->董事会”。如有需要,可改为“董事-->董事长-->董事会”。 ”,该方案只能通过修改源码来实现,客户端无法自定义审批流程。
责任链模式概述
在许多情况下,软件系统中的一个请求可以由多个对象来处理。例如,SCM系统中的采购订单审批,董事、副董事长、董事长、董事会都可以处理采购订单。他们可以形成一个链接来处理采购订单。采购订单沿着这条链传输,这条链称为责任链。责任链可以是直线、环形或树形结构。最常见的责任链是一条直线,即请求沿着单向链传递。链上的每个对象都是一个请求处理程序。责任链模型可以将请求处理程序组织成一条链,让请求沿着这条链传递。链上的处理程序相应地处理请求。客户端无需关心请求处理和请求传递的细节,只需将请求发送到链上,即可解耦请求发送者和请求处理者。
责任链模式定义如下:
舜宇软件公司承担了某企业SCM(供应链管理)系统的开发任务,其中包括采购审批子系统。公司采购审批采取分级方式进行,即根据采购金额由各级主管审批。董事可以批准5万元以下(不含5万元)的采购订单,副董事长可以批准5万元以下的采购订单。对于10万元至10万元(不含10万元)的采购订单,董事长可以批准10万元至50万元(不含50万元)的采购订单。对于人民币50万元及以上的采购订单,需要召开董事会会议。讨论并决定。如图16-1所示:
图16-1 采购订单分级审批示意图
责任链模式:避免请求发送者和接收者耦合,让多个对象可以接收请求,将这些对象连接成一条链,并沿着这条链传递请求,直到有对象处理为止。责任链模式是一种对象行为模式。
责任链模式结构的核心是抽象处理器的引入。责任链模型的结构如图16-2所示:
责任链模式结构图包括以下角色:
● Handler(抽象处理程序):定义处理请求的接口。它通常被设计为抽象类。由于不同的具体处理程序处理请求的方式不同,因此在其中定义了抽象的请求处理方法。由于每个处理器的下一个处理器仍然是一个处理器,因此在抽象处理器中定义了一个抽象处理器类型对象(如结构图中的后继者)作为其对下一个处理器的引用。通过这个引用,处理程序可以链接成一条链。
●ConcreteHandler(具体处理程序):是抽象处理程序的子类,可以处理用户请求。抽象处理程序中定义的抽象请求处理方法在具体处理程序类中实现。在处理请求之前需要进行判断。查看是否有相应的处理权限,如果可以处理请求,则处理,否则将请求转发给后继者;在特定的处理器中,可以访问链中的下一个对象,以方便请求的转发。
在责任链模式中,许多对象通过每个对象对其下级的引用连接起来形成一条链。请求沿着链向上传递,直到链中的对象决定处理该请求为止。发出请求的客户端并不知道链中的哪个对象最终处理该请求,这使得系统可以动态地重新组织链并分配责任,而不会影响客户端。
责任链模式的核心在于抽象处理程序类的设计。抽象处理程序的典型代码如下:
[java]查看纯文本
抽象类处理程序{
//维护对下一个家的引用
受保护的处理程序后继者;
publicvoid setSuccessor(处理程序后继者) {
this.后继者=后继者;
publicabstractvoidhandleRequest(字符串请求);
在上面的代码中,抽象处理器类定义了下一个提供者的引用对象,以便将请求转发到下一个提供者。该对象的访问器可以设置为受保护,并且可以在其子类中使用。抽象请求处理方法在抽象处理程序类中声明,具体实现留给子类。
具体处理器是抽象处理器的子类。它有两个主要功能:第一个是处理请求。不同的具体处理器以不同的形式实现抽象请求处理方法handleRequest();二是转发请求。如果请求超出了当前处理器类的权限,则可以将请求转发到下一个处理器。特定处理程序类的典型代码如下:
[java]
类 ConcreteHandler 扩展了 Handler {
publicvoidhandleRequest(字符串请求){
if(请求满足条件){
//处理请求
别的 {
this.successor.handleRequest(请求); //转发请求
通过判断具体处理类中的请求即可做出相应的处理。
需要注意的是,责任链模型并不创建责任链。责任链的创建必须由系统的其他部分来完成。通常,责任链是在使用责任链的客户端中创建的。责任链模式减少了请求的发送者和接收者之间的耦合,允许多个对象有机会处理请求。
完整的解决方案
为了让采购订单审批流程更加灵活,实现采购订单的链式传输和处理,Sunny开发人员采用责任链模型,实现采购订单的分级审批。其基本结构如图16-3所示:
在图16-3中,抽象类Approver充当抽象处理程序(抽象交付者),Director、VicePresident、President和Congress充当具体处理程序(具体交付者),PurchaseRequest充当请求类。完整代码如下:
[java]
//采购订单:请求类
类购买请求{
私人双倍金额; //购买金额
私有整数; //采购订单号
私有字符串用途; //购买目的
公共PurchaseRequest(双倍金额,整数,字符串目的){
this.金额 = 金额;
this.number = 数字;
this.目的=目的;
publicvoid setAmount(双倍金额) {
this.金额 = 金额;
公共双 getAmount() {
返回这个金额;
publicvoid setNumber(int number) {
this.number = 数字;
publicint getNumber() {
返回这个数字;
publicvoid setPurpose(字符串目的){
this.目的=目的;
公共字符串 getPurpose() {
returnthis.目的;
//审批者类:抽象处理器
抽象类审批者{
受保护的批准人继任者; //定义后继对象
受保护的字符串名称; //审批人姓名
公共批准者(字符串名称){
this.name = 名称;
//设置后继者
publicvoid setSuccessor(审批人后继者) {
this.successor = 后继者;
//抽象请求处理方法
publicabstractvoid processRequest(PurchaseRequest 请求);
//Director类:具体的handler
类主管延伸审批者{
公共董事(字符串名称){
超级(名称);
//具体请求处理方法
publicvoid processRequest(PurchaseRequest 请求) {
if (request.getAmount() < 50000) {
System.out.println("总监" + this.name + "审批采购订单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购用途:" + request.getPurpose () + “.”); //处理请求
别的 {
this.successor.processRequest(请求); //转发请求
//副主席类:具体的handler
类 VicePresident 扩展 Approver {
公共副总统(字符串名称){
超级(名称);
//具体请求处理方法
publicvoid processRequest(PurchaseRequest 请求) {
if (request.getAmount() < 100000) {
System.out.println("副董事长" + this.name + "审批采购订单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购用途:" + request .getPurpose() + "."); //处理请求
别的 {
this.successor.processRequest(请求); //转发请求
//Chairman类:具体的handler
类 President 扩展 Approver {
公共总统(字符串名称){
超级(名称);
//具体请求处理方法
publicvoid processRequest(PurchaseRequest 请求) {
if (request.getAmount() < 500000) {
System.out.println("董事长" + this.name + "审批采购订单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购用途:" + request.getPurpose () + “.”); //处理请求
别的 {
this.successor.processRequest(请求); //转发请求
//Board类:具体的handler
类国会扩展批准者{
公开国会(字符串名称){
超级(名称);
//具体请求处理方法
publicvoid processRequest(PurchaseRequest 请求) {
System.out.println("召开董事会批准采购订单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购用途:" + request.getPurpose( )+“。”); //处理请求
编写如下客户端测试代码:
[java]查看纯文本
类客户端{
publicstaticvoid main(String[] args) {
批准人wjzhang,gyang,jguo,会议;
wjzhang = new Director("张无忌");
gyang = new VicePresident("杨果");
jguo = new President("郭靖");
会议=新的国会(“董事会”);
//创建责任链
wjzhang.setSuccessor(gyang);
gyang.setSuccessor(jguo);
jguo.setSuccessor(会议);
//创建采购订单
PurchaseRequest pr1 = new PurchaseRequest(45000,10001,"购买倚天剑");
wjzhang.processRequest(pr1);
PurchaseRequest pr2 = new PurchaseRequest(60000,10002,"购买《向日葵收藏》);
wjzhang.processRequest(pr2);
PurchaseRequest pr3 = new PurchaseRequest(160000,10003,"购买《金刚经》);
wjzhang.processRequest(pr3);
PurchaseRequest pr4 = new PurchaseRequest(800000,10004,"购买桃花岛");
wjzhang.processRequest(pr4);
编译并运行程序,输出结果如下:
张无忌主任批准采购订单:10001份,金额:45,000.0元,采购目的:购买倚天剑。
杨过副会长批准采购订单:10002,金额:60,000.0元,采购目的:购买《向日葵藏品》。
董事长郭靖批准采购订单:10003,金额:160,000.0元,采购目的:购买《金刚经》。
董事会审议通过采购订单:10004,金额:800,000.0元,采购目的:购买桃花岛。
如果需要在系统中添加新的特定处理器,比如添加一个可以审批5万元到8万元(不含8万元)采购订单的经理角色,则需要编写一个新的特定处理器类 Manager ,作为抽象处理器类Approver的子类,实现了Approver类中定义的抽象处理方法。如果购买金额大于或等于80,000元,则请求将转发给下一方。代码如下:
[java]
//Manager类:具体的handler
类 Manager 扩展 Approver {
公共经理(字符串名称){
超级(名称);
//具体请求处理方法
publicvoid processRequest(PurchaseRequest 请求) {
if (request.getAmount() < 80000) {
System.out.println("经理" + this.name + "审批采购订单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购用途:" + request.getPurpose () + “.”); //处理请求
别的 {
this.successor.processRequest(请求); //转发请求
由于链创建过程由客户端负责,因此添加新的具体处理程序类不会对原有类库产生影响,也无需修改现有类的源代码,符合“开闭原则” 。
在客户端代码中,如果要向系统应用新的特定请求处理程序,则需要创建新的特定处理程序对象,然后将该对象添加到责任链中。例如,在客户端测试代码中添加以下代码:
[java]
批准人 黄;
rhuang = new Manager("黄蓉");
将链接构建代码更改为:
[java]
//创建责任链
wjzhang.setSuccessor(rhuang); //设置“黄蓉”为“张无忌”的下一任拥有者
rhuang.setSuccessor(gyang); //设置“杨过”为“黄蓉”的下一个家族
gyang.setSuccessor(jguo);
jguo.setSuccessor(会议);
重新编译并运行程序,输出结果如下:
张无忌主任批准采购订单:10001份,金额:45,000.0元,采购目的:购买倚天剑。
黄蓉经理批准采购订单:10002,金额:60,000.0元,采购目的:采购《向日葵珍藏》。
董事长郭靖批准采购订单:10003,金额:160,000.0元,采购目的:购买《金刚经》。
董事会审议通过采购订单:10004,金额:800,000.0元,采购目的:购买桃花岛。
纯责任链模型和非纯责任链模型
责任链模型可以分为纯责任链模型和非纯责任链模型两种:
(1)纯责任链模式
纯责任链模型要求特定的处理者对象只能选择两种操作之一:要么承担所有责任,要么将责任传递给下一方。它不允许特定的处理器对象承担部分或全部责任。责任一个接一个地传递。而且,在纯责任链模型中,要求请求必须被某个处理程序对象接收,并且不能出现请求没有被任何处理程序对象处理的情况。在前面的采购订单审批示例中,应用了纯责任链模型。
(2)不纯粹的责任链模型
在不纯的责任链模型中,一个请求被允许由特定的处理器部分处理然后向下传递,或者特定的处理器处理完一个请求后,其后续的处理器可以继续处理该请求,而一个请求最终不能继续处理。由任何处理程序对象接收。 Java AWT 1.0中的事件处理模型应用了不纯的责任链模型。它的基本原理是这样的:由于窗口组件(如按钮、文本框等)一般位于容器组件中,当某个组件上发生事件时,首先通过handleEvent将事件传递给相应的事件处理方法组件对象的 () 方法。处理方法会处理这个事件,然后决定是否将事件传播到上层容器组件;上层容器组件接收到事件后,可以继续处理该事件并决定是否继续向上层容器组件传播,以此类推,直到事件到达顶层容器组件为止;如果直到传递到顶层容器仍然没有处理方法,则该事件将不会被处理。当每一级组件接收到一个事件时,它都可以处理该事件,无论该事件是否在上一级已经处理过,或者是否还没有处理过。显然,这是一个不纯粹的责任链模型。早期Java AWT事件模型(JDK 1.0及更早版本)中的这种事件处理机制也称为事件冒泡机制。从Java.1.1开始,JDK使用观察者模式而不是责任链模式来处理事件。目前,这种事件浮动机制仍然可以用于Java中的事件处理。
16.5 责任链模型总结
责任链模式通过建立一条链来组织请求处理程序。请求将沿着链传递。请求发送者不需要知道请求何时、何地、如何被处理,实现了请求发送者与处理者的解耦。 。在软件开发中,如果遇到多个对象可以处理同一个请求,可以应用责任链模式。例如,在Web应用程序开发中,创建过滤器链来过滤请求数据并在工作流系统中实现。公文的分级审批等,利用责任链模型可以更好的解决。
一、主要优点
责任链模型的主要优点如下:
(1) 责任链模型意味着一个对象不需要知道哪个其他对象处理它的请求。对象只需要知道请求将被处理。接收方和发送方都没有明确的对方信息,链中的对象不需要知道链的结构,客户端负责链的创建,降低了链的耦合性。系统。
(2)请求处理对象只需要维护对其后继者的引用,而不需要维护其对所有候选处理器的引用,这样可以简化对象的互连。
(3)在给对象分配职责时,职责链可以给我们带来更大的灵活性。我们可以通过在运行时动态添加或修改链来添加或更改处理请求的职责。
(4)当向系统添加新的特定请求处理程序时,无需修改原系统的代码。只需要在客户端重新建链即可。从这一点来看,是符合“开闭原则”的。
2.主要缺点
责任链模型的主要缺点如下:
(1) 由于请求没有明确的接收者,因此无法保证该请求一定会得到处理。直到链的末尾,请求才可能被处理;由于责任链配置不正确,请求也可能被处理。来处理。
(2)对于职责链比较长的情况,请求处理可能涉及多个处理对象,系统性能会受到一定影响,并且代码调试时不方便。
(3)如果链接搭建不当,可能会造成循环调用,导致系统陷入死循环。
3、适用场景
在以下情况下考虑使用责任链模式:
(1) 有多个对象可以处理同一个请求。处理请求的具体对象将在运行时确定。客户端只需要向链上提交请求即可,无需关心请求由谁处理以及如何处理。的。
(2) 在未明确指定接收者的情况下向多个对象之一提交请求。
(3)可以动态指定一组对象来处理请求。客户端可以动态创建职责链来处理请求,还可以更改链中处理器之间的顺序。