【设计模式——学习笔记】23种设计模式——职责链/责任链模式(Chain of Responsibility)(原理讲解+应用场景介绍+案例介绍+Java代码实现)

简介: 【设计模式——学习笔记】23种设计模式——职责链/责任链模式(Chain of Responsibility)(原理讲解+应用场景介绍+案例介绍+Java代码实现)

案例引入

学校OA系统的采购审批项目: 需求是

  • 采购员采购教学器材
  • 如果金额 小于等于5000(0<x<=5000),由教学主任审批
  • 如果金额 小于等于10000(5000<x<=10000),由院长审批
  • 如果金额 小于等于30000(10000<x<=30000),由副校长审批
  • 如果金额 超过30000以上(30000<x),由校长审批

传统方式实现

创建一个不同的审批人对应的类,在客户端中写 if else 判断程序,符合不同的条件,就让不同的审批人去处理

分析

客户端这里会使用到分支判断(比如switch)来对不同的采购请求处理, 这样就存在如下问题

  • 如果各个级别的人员审批金额发生变化,客户端的代码也需要发生变化
  • 客户端必须明确知道有多少个审批级别及其访问方式,这样对一个采购请求进行处理的类和Approver (审批人) 就存在强合耦合关系,不利于代码的扩展和维护

【改进】

使用职责链模式

介绍

基础介绍

  • 职责链模式又叫责任链模式,为请求创建了一个接收者对象的链(如下方的简单示意图)。这种模式对请求的发送者和接收者进行解耦。当发送者发送一个请求之后,接收者会按照职责链的顺序一个一个地找出到底应该由谁来负责处理这个请求
  • 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推,如果所有人都不能处理,最后就会提示或者报错说不能处理
  • 这种类型的设计模式属于行为型模式

登场角色

  • Handler(抽象处理者):定义了一个处理请求的接口,同时设有变量来保存其他的Handler
  • ConcreteHandler(具体处理者):处理它自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前请求则处理,否则就将该请求交给后继者去处理,从而形成一个职责链
  • Request(请求对象):含有很多属性,表示一个请求
  • Client(请求者):发送请求

应用场景

  • 有多个对象可以处理同一个请求时,比如: 多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器

案例实现

案例一

类图

实现

【采购请求】

package com.atguigu.responsibilitychain;
/**
 * 请求类
 */
public class PurchaseRequest {
   /**
    * 请求类型
    */
   private int type = 0;
   /**
    * 请求金额
    */
   private float price = 0.0f;
   private int id = 0;
   /**
    * 构造器
    * @param type
    * @param price
    * @param id
    */
   public PurchaseRequest(int type, float price, int id) {
      this.type = type;
      this.price = price;
      this.id = id;
   }
   public int getType() {
      return type;
   }
   public float getPrice() {
      return price;
   }
   public int getId() {
      return id;
   }
}

【抽象请求处理者】

package com.atguigu.responsibilitychain;
/**
 * 请求处理者
 */
public abstract class Approver {
   /**
    * 下一个处理者
    */
   Approver approver;
   /**
    * 当前处理者名字
    */
   String name;
   public Approver(String name) {
      this.name = name;
   }
   /**
    * 下一个处理者
    * @param approver
    */
   public void setApprover(Approver approver) {
      this.approver = approver;
   }
   /**
    * 处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
    * @param purchaseRequest
    */
   public abstract void processRequest(PurchaseRequest purchaseRequest);
}

【系级处理人】

package com.atguigu.responsibilitychain;
public class DepartmentApprover extends Approver {
   public DepartmentApprover(String name) {
      super(name);
   }
   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
      if(purchaseRequest.getPrice() <= 5000) {
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      }else {
         approver.processRequest(purchaseRequest);
      }
   }
}

【学院级别处理人】

package com.atguigu.responsibilitychain;
public class CollegeApprover extends Approver {
   public CollegeApprover(String name) {
      super(name);
   }
   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
      if(purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) {
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      }else {
         approver.processRequest(purchaseRequest);
      }
   }
}

【副校长】

package com.atguigu.responsibilitychain;
public class ViceSchoolMasterApprover extends Approver {
   public ViceSchoolMasterApprover(String name) {
      super(name);
   }
   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
      if(purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      }else {
         approver.processRequest(purchaseRequest);
      }
   }
}

【校长】

package com.atguigu.responsibilitychain;
public class SchoolMasterApprover extends Approver {
   public SchoolMasterApprover(String name) {
      super(name);
   }
   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
      if(purchaseRequest.getPrice() > 30000) {
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      }else {
         approver.processRequest(purchaseRequest);
      }
   }
}

【客户端】

package com.atguigu.responsibilitychain;
public class Client {
   public static void main(String[] args) {
      //创建一个请求
      PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);
      //创建相关的审批人
      DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
      CollegeApprover collegeApprover = new CollegeApprover("李院长");
      ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校");
      SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");
      //需要将各个审批级别的下一个设置好 (处理人构成环形)
      departmentApprover.setApprover(collegeApprover);
      collegeApprover.setApprover(viceSchoolMasterApprover);
      viceSchoolMasterApprover.setApprover(schoolMasterApprover);
      //防止一千块钱也来找校长处理,找校长的话,校长就去找手下处理
      schoolMasterApprover.setApprover(departmentApprover);
      //找谁开始调用都可以实现功能
      departmentApprover.processRequest(purchaseRequest);
      viceSchoolMasterApprover.processRequest(purchaseRequest);
   }
}

【主类】

 请求编号 id= 1 被 佟校长 处理
 请求编号 id= 1 被 佟校长 处理
Process finished with exit code 0

案例二

类图

实现

【问题类】

package com.atguigu.responsibilitychain.Sample;
public class Trouble {
    /**
     * 问题编号
     */
    private int number;
    /**
     * 根据编号生成问题
     * @param number
     */
    public Trouble(int number) {    
        this.number = number;
    }
    /**
     * 获取问题编号
     * @return
     */
    public int getNumber() {        
        return number;
    }
    /**
     * 代表问题的字符串
     * @return
     */
    public String toString() {      
        return "[Trouble " + number + "]";
    }
}

【用来解决问题的抽象类】

package com.atguigu.responsibilitychain.Sample;
public abstract class Support {
    /**
     * 解决问题的实例的名字
     */
    private String name;
    /**
     * 要推卸给的对象
     */
    private Support next;
    /**
     * 生成解决问题的实例
     *
     * @param name
     */
    public Support(String name) {
        this.name = name;
    }
    /**
     * 设置要推卸给的对象
     *
     * @param next
     * @return
     */
    public Support setNext(Support next) {
        this.next = next;
        return next;
    }
    /**
     * 解决问题的步骤
     * 调用了抽象方法resolve,这里属于模板方法模式
     * @param trouble
     */
    public void support(Trouble trouble) {
        if (resolve(trouble)) {
            done(trouble);
        } else if (next != null) {
            // 问题还没有解决,让下一个对象来尝试解决
            next.support(trouble);
        } else {
            // 所有对象都没有办法解决该问题,提示用户 问题没办法解决
            fail(trouble);
        }
    }
    /**
     * 显示字符串
     *
     * @return
     */
    public String toString() {
        return "[" + name + "]";
    }
    /**
     * 解决问题的方法
     * 需要子类去具体实现,如果解决了问题,就返回true
     *
     * @param trouble
     * @return
     */
    protected abstract boolean resolve(Trouble trouble);
    /**
     * 解决
     *
     * @param trouble
     */
    protected void done(Trouble trouble) {
        System.out.println(trouble + " is resolved by " + this + ".");
    }
    /**
     * 未解决
     *
     * @param trouble
     */
    protected void fail(Trouble trouble) {
        System.out.println(trouble + " cannot be resolved.");
    }
}

【不解决问题的类】

package com.atguigu.responsibilitychain.Sample;
public class NoSupport extends Support {
    public NoSupport(String name) {
        super(name);
    }
    /**
     * 解决问题的方法 
     * 什么都解决不了,直接返回false
     * @param trouble
     * @return
     */
    protected boolean resolve(Trouble trouble) {     
        return false; 
    }
}

【具体解决问题的类:只要问题的编号小于limit,就可以解决】

package com.atguigu.responsibilitychain.Sample;
/**
 * 可以解决编号小于limit的问题
 */
public class LimitSupport extends Support {
    private int limit;
    /**
     * 构造函数
     *
     * @param name
     * @param limit
     */
    public LimitSupport(String name, int limit) {
        super(name);
        this.limit = limit;
    }
    protected boolean resolve(Trouble trouble) {
        if (trouble.getNumber() < limit) {
            /*
            解决了什么什么问题
             */
            return true;
        } else {
            return false;
        }
    }
} 

【具体解决问题的类:只要问题的编号是奇数,就可以解决】

package com.atguigu.responsibilitychain.Sample;
public class OddSupport extends Support {
    /**
     * 构造函数
     * @param name
     */
    public OddSupport(String name) {                
        super(name);
    }
    protected boolean resolve(Trouble trouble) {    
        if (trouble.getNumber() % 2 == 1) {
            return true;
        } else {
            return false;
        }
    }
}

【具体解决问题的类:只能解决指定编号的问题】

package com.atguigu.responsibilitychain.Sample;
public class SpecialSupport extends Support {
    private int number;
    /**
     * 构造函数
     *
     * @param name
     * @param number
     */
    public SpecialSupport(String name, int number) {
        super(name);
        this.number = number;
    }
    /**
     * 解决问题的方法
     *
     * @param trouble
     * @return
     */
    protected boolean resolve(Trouble trouble) {
        if (trouble.getNumber() == number) {
            return true;
        } else {
            return false;
        }
    }
}

【主类】

package com.atguigu.responsibilitychain.Sample;
public class Main {
    public static void main(String[] args) {
        // 生成六个解决问题的实例
        Support alice = new NoSupport("Alice");
        Support bob = new LimitSupport("Bob", 100);
        Support charlie = new SpecialSupport("Charlie", 429);
        Support diana = new LimitSupport("Diana", 200);
        Support elmo = new OddSupport("Elmo");
        Support fred = new LimitSupport("Fred", 300);
        // 连接对象,形成职责链
        alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
        // 制造各种问题,增长步长大一点,让问题的编号更加的分散
        for (int i = 0; i < 500; i += 33) {
            alice.support(new Trouble(i));
        }
    }
}

【运行】

[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].
Process finished with exit code 0

职责链模式在Spring中的应用

  • springmvc请求的流程图中,执行了拦截器相关方法interceptor.preHandler等等,在处理SpringMvc请求时,使用到职责链模式,还使用到适配器模式
  • HandlerExecutionChain主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
  • HandlerExecutionChain维护了Handlerlnterceptor的集合, 可以向其中注册相应的拦截器

总结

【优点】

  • 将请求和处理分开,实现解耦,提高系统的灵活性
  • 简化了对象,使对象不需要知道链的结构
  • 可以动态地调整链的结构
  • 让每一个ConcreteHandler更加专注于己的工作,程序的逻辑更加清晰

【缺点】

  • 性能会受到影响,特别是在链比较长的时候。因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值超过则不允许该链建立,避免出现超长链破坏系统性能
  • 调试不方便,采用了类似递归的方式,调试时逻辑可能比较复杂

【问答】

在视窗系统中,经常使用职责链模式。在视窗系统的窗口中,有按钮和文本输入框、勾选框等组件(也称为部件或控件)。当点击鼠标时,鼠标点击事件的处理是如何传播的呢? 职责链模式中的next(要推卸给的对象)是哪个组件呢?


答:一般next字段中保存的多是控件的父窗口


如下图中的小对话框。当焦点移动至“字体”列表框上时,按下键盘上的↑↓键可以选择相应的字体。但是,当焦点移动至“显示均衡字体”勾选框上时如果按下↑键,焦点会移动至“字体”列表框,之后,即使按下↓键,焦点也不会返回到勾选框上。请运用Chain ofResponsibility模式的思考方法来说明这个问题

如果焦点在列表框中,列表框会自己处理↑↓键被按下的事件,不会将请求推卸给next,但如果焦点在勾选框中,它则不会自己处理↑↓小键被按下的事件,而是将请求推卸给next所对应的父对话框。当父对话框接收到↑↓键被按下的事件时,会将焦点移动至列表框中

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面
目录
相关文章
|
设计模式 算法 前端开发
【设计模式】【行为型模式】职责链模式(Chain of Responsibility)
一、入门 什么是职责链模式? 职责链模式是一种行为设计模式,它允许你将请求沿着一条链传递,直到有对象处理它为止。每个对象都有机会处理请求,或者将其传递给链中的下一个对象。 为什么需要职责链模式? 使用
454 16
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
337 4
|
设计模式 算法 搜索推荐
PHP中的设计模式:提高代码可维护性的秘诀
在本文中,我们将探讨PHP设计模式的重要性以及它们如何帮助开发者编写出更加灵活、可维护的代码。我们将介绍几种常见的设计模式,包括单例模式、工厂模式和策略模式,并通过实际示例展示它们在PHP中的应用。最后,我们会讨论何时使用这些设计模式以及在实际项目开发中的最佳实践。
268 28
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性和扩展性
【10月更文挑战第13天】 本文将探讨PHP中常见的设计模式及其在实际项目中的应用。通过对比传统编程方式,我们将展示设计模式如何有效地提高代码的可维护性和扩展性。无论是单例模式确保类的单一实例,还是观察者模式实现对象间的松耦合,每一种设计模式都为开发者提供了解决特定问题的最佳实践。阅读本文后,读者将能更好地理解和应用这些设计模式,从而提升PHP编程的效率和质量。
|
设计模式 Java Kotlin
Kotlin学习笔记 - 改良设计模式 - 迭代器模式
Kotlin学习笔记 - 改良设计模式 - 迭代器模式
185 2
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性
设计模式在PHP开发中至关重要,如单例模式确保类仅有一个实例并提供全局访问点,适用于管理数据库连接或日志记录。工厂模式封装对象创建过程,降低系统耦合度;策略模式定义算法系列并使其可互换,便于实现不同算法间的切换。合理选择设计模式需基于需求分析,考虑系统架构,并通过测试驱动开发验证有效性,确保团队协作一致性和代码持续优化。设计模式能显著提升代码质量,解决开发中的设计难题。
203 9
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP开发领域,设计模式是解决常见问题的高效方案集合。它们不是具体的代码,而是一种编码和设计经验的总结。单例模式作为设计模式中的一种,确保了一个类仅有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的基本概念、实现方式及其在PHP中的应用。
单例模式在PHP中的应用广泛,尤其在处理数据库连接、日志记录等场景时,能显著提高资源利用率和执行效率。本文从单例模式的定义出发,详细解释了其在PHP中的不同实现方法,并探讨了使用单例模式的优势与注意事项。通过对示例代码的分析,读者将能够理解如何在PHP项目中有效应用单例模式。
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入探索与实践在软件开发的广袤天地中,PHP以其独特的魅力和强大的功能,成为无数开发者手中的得力工具。而在这条充满挑战与机遇的征途上,设计模式犹如一盏明灯,指引着我们穿越代码的迷雾,编写出更加高效、灵活且易于维护的程序。今天,就让我们聚焦于设计模式中的璀璨明珠——策略模式,深入探讨其在PHP中的实现方法及其实际应用价值。
策略模式,这一设计模式的核心在于它为软件设计带来了一种全新的视角和方法。它允许我们在运行时根据不同情况选择最适合的解决方案,从而极大地提高了程序的灵活性和可扩展性。在PHP这门广泛应用的编程语言中,策略模式同样大放异彩,为开发者们提供了丰富的创作空间。本文将从策略模式的基本概念入手,逐步深入到PHP中的实现细节,并通过一个具体的实例来展示其在实际项目中的应用效果。我们还将探讨策略模式的优势以及在实际应用中可能遇到的挑战和解决方案,为PHP开发者提供一份宝贵的参考。