【设计模式-11】责任链模式

news/2024/7/7 19:47:17

认识设计模式(十一)---责任链模式

  • 【一】责任链模式
  • 【二】介绍
    • (1)意图
    • (2)主要解决
    • (3)何时使用
    • (4)如何解决
    • (5)关键代码
    • (6)应用实例
    • (7)优点
    • (8)缺点
    • (9)使用场景
    • (10)注意事项
  • 【三】实现案例
    • (1)介绍
    • (2)创建抽象的记录器类
    • (3)创建扩展了该记录器类的实体类
    • (4)创建不同类型的记录器
  • 【四】更多案例介绍
    • (1)责任链模式解决采购审批问题
      • 1-创建采购请求的实体类
      • 2-创建抽象的采购审批类
      • 3-创建扩展了该审批类的实体类
      • 4-设置责任链,测试类
    • (2)责任链模式解决登录过程的数据校验问题
      • 1-创建一个实体类Member
      • 2-传统方式的登录校验代码
      • 3-开始使用责任链模式优化,创建抽象的处理类Handler
      • 4-创建扩展了处理类的实体类
      • 5-在服务层设置链
      • 6-测试代码
      • 7-责任链模式+建造者模式结合使用优化

【一】责任链模式

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

【二】介绍

(1)意图

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

(2)主要解决

职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

(3)何时使用

在处理消息的时候以过滤很多道。

(4)如何解决

拦截的类都实现统一接口。

(5)关键代码

Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

(6)应用实例

1、红楼梦中的"击鼓传花"。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

(7)优点

1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

(8)缺点

1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

(9)使用场景

1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。

(10)注意事项

在 JAVA WEB 中遇到很多应用。

【三】实现案例

(1)介绍

我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
在这里插入图片描述

(2)创建抽象的记录器类


public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;
 
   protected int level;
 
   //责任链中的下一个元素
   protected AbstractLogger nextLogger;
 
   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }
 
   public void logMessage(int level, String message){
      // 如果传的参数level大于等于已有的level,则写日志
      if(this.level <= level){
         write(message);
      }
      // 如果下一个记录器不为null
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }
 
   abstract protected void write(String message);
   
}

(3)创建扩展了该记录器类的实体类

public class ConsoleLogger extends AbstractLogger {
 
   public ConsoleLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("Standard Console::Logger: " + message);
   }
}


public class ErrorLogger extends AbstractLogger {
 
   public ErrorLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("Error Console::Logger: " + message);
   }
}


public class FileLogger extends AbstractLogger {
 
   public FileLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("File::Logger: " + message);
   }
}

(4)创建不同类型的记录器

创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。


public class ChainPatternDemo {
   
   private static AbstractLogger getChainOfLoggers(){
 
      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
 
      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);
 
      return errorLogger;  
   }
 
   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();
 
      loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
 
      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "This is a debug level information.");
 
      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}

输出结果

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

【四】更多案例介绍

(1)责任链模式解决采购审批问题

1-创建采购请求的实体类

/**
 * @ClassName: PurchaseRequest
 * @Author: AllenSun
 * @Date: 2020/3/9 22:36
 */
public class PurchaseRequest {

    private int type=0;
    private float price=0.0f;
    private int id=0;

    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;
    }
}

2-创建抽象的采购审批类

/**
 * @ClassName: Approver
 * @Author: AllenSun
 * @Date: 2020/3/9 22:31
 */
public abstract class Approver {
    //下一个处理者
    Approver approver;
    //名字
    String name;

    public Approver(String name) {
        this.name = name;
    }

    //下一个处理者
    public void setApprover(Approver approver) {
        this.approver = approver;
    }

    //处理审批请求的方法,得到一个请求,处理是子类完成,因此该方法做成抽象
    public abstract void processRequest(PurchaseRequest purchaseRequest);
}

3-创建扩展了该审批类的实体类

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
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);
        }
    }
}

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
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);
        }

    }
}

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
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);
        }
    }
}

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
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);
        }
    }
}

4-设置责任链,测试类

/**
 * @ClassName: Client
 * @Author: AllenSun
 * @Date: 2020/3/9 22:50
 */
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);
    }
}

(2)责任链模式解决登录过程的数据校验问题

在日常生活中责任链模式还是比较常见的,我们平时工作处理一些事务,往往是各部门协同合作的完成—个任务。而每个部门都有各自的职责,因此,很多时候事谓完成—半,便会转交给下—个部门,直到所有部门都过一渔之后事谓才能完成。

1-创建一个实体类Member

@Data
public class Member {
    private String loginName;
    private String loginPass;
    private String roleName;

    public Member(String loginName, String loginPass) {
        this.loginName = loginName;
        this.loginPass = loginPass;
    }
}

2-传统方式的登录校验代码

public class MemberService {
    public static void main(String[] args) {
        MemberService service = new MemberService();
        service.login("tom", "666");
    }

    public void login(String loginName, String loginPass) {
        if (StringUtils.isEmpty(loginName) ||
                StringUtils.isEmpty(loginPass)) {
            System.out.println("用户名和密码为空");
            return;
        }
        System.out.println("用户名和密码不为空,可以往下执行");
        Member member = checkExists(loginName, loginPass);
        if (null == member) {
            System.out.println("用户不存在");
            return;
        }
        System.out.println("登录成功!");
        if (!"管理员".equals(member.getRoleName())) {
            System.out.println("您不是管理员,没有操作权限");
            return;
        }
        System.out.println("允许操作");
    }

    private Member checkExists(String loginName, String loginPass) {
        Member member = new Member(loginName, loginPass);
        member.setRoleName("管理员");
        return member;
    }
}

3-开始使用责任链模式优化,创建抽象的处理类Handler

public abstract class Handler {
    protected Handler next;
    public void next(Handler next){ this.next = next;}
    public abstract void doHandler(Member member);
}

4-创建扩展了处理类的实体类

我们分别创建非空校验ValidateHandler类、登录校验LoginHandler类和权限校验AuthHandler类

public class ValidateHandler extends Handler {
    public void doHandler(Member member) {
        if (StringUtils.isEmpty(member.getLoginName()) ||
                StringUtils.isEmpty(member.getLoginPass())) {
            System.out.println("用户名和密码为空");
            return;
        }
        System.out.println("用户名和密码不为空,可以往下执行");
        next.doHandler(member);
    }
}

public class LoginHandler extends Handler {
    public void doHandler(Member member) {
        System.out.println("登录成功!");
        member.setRoleName("管理员");
        next.doHandler(member);
    }
}

public class AuthHandler extends Handler {
    public void doHandler(Member member) {
        if (!"管理员".equals(member.getRoleName())) {
            System.out.println("您不是管理员,没有操作权限");
            return;
        }
        System.out.println("允许操作");
    }
}

5-在服务层设置链

public class MemberService {

    public void login(String loginName, String loginPass) {
        Handler validateHandler = new ValidateHandler();
        Handler loginHandler = new LoginHandler();
        Handler authHandler = new AuthHandler();
        validateHandler.next(loginHandler);
        loginHandler.next(authHandler);
        validateHandler.doHandler(new Member(loginName, loginPass));
    }
}

6-测试代码

public class Test {
    public static void main(String[] args) {
        MemberService memberService = new MemberService();
        memberService.login("tom","666");
    }
}

测试结果

用户名和密码校验成功,可以往下执行
登录成功!
您是管理员,允许操作

7-责任链模式+建造者模式结合使用优化

因为责任链模式具备链式结构,而上面代码中,我们看到,负责组装链式结构的角色是
MemberService ,当链式结构较长时,MemberService的工作会非常繁琐,并且MemberService代码相对臃肿,且后续更改处理者或消息类型时,都必须在MemberService中进行修改,不符合开闭原则。产生这些问题的原因就是因为链式结构的组装过于复杂,而对于复杂结构的创建,很自然我们就会想到建造者模式,使用建造者模式,我们完全可以MemberService指定的处理节点对象进行自动链式组装,客户只需指定处理节点对象,其他任何事情都无需关心,并且客户指定的处理节点对象顺序不同 ,构造出来的链式结构也随之不同。我们来改造一下,修改Handler的代码:

public abstract class Handler<T> {
    protected Handler next;
    //public void next(Handler next){ this.next = next;}
    private void next(Handler next){ this.next = next;}

    public abstract void doHandler(Member member);

    public static class Builder<T>{
        private Handler<T> head;
        private Handler<T> tail;

        public Builder<T> addHandler(Handler handler){
           // do {
                if (this.head == null) {
                    this.head = this.tail = handler;
                    return this;
                }
                this.tail.next(handler);
                this.tail = handler;
           // }while (false);//真正框架中,如果是双向链表,会判断是否已经到了尾部
            return this;
        }

        public Handler<T> build(){
            return this.head;
        }
    }
}

然后 ,修改MemberService的代码:

public class MemberService {
    public void login(String loginName,String loginPass){
        Handler.Builder builder = new Handler.Builder();
        builder.addHandler(new ValidateHandler())
               .addHandler(new LoginHandler())
               .addHandler(new AuthHandler());
        builder.build().doHandler(new Member(loginName,loginPass));
        //用过Netty的人,肯定见过
    }
}

http://lihuaxi.xjx100.cn/news/873245.html

相关文章

【openGauss实战9】深度分析分区表

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

centos学习记录

遇到的问题及其解决办法 centos7安装图形化界面 yum groupinstall ‘X Window System’ yum groupinstall -y ‘GNOME Desktop’ 安装完成后输入init 5进入图形化界面 centos7安装vmware-tools 第一步卸载open-vm-tools 输入命令 yum remove open-vm-tools 输入命令 reboot 在…

全网详细解读基于java调用ChatGPT的API接口

文章目录1. 文章引言2. 基于java调用API2.1 环境配置2.2 编写代码3. 重要总结3.1 官网链接地址3.2 开发语言的示例链接1. 文章引言 首先&#xff0c;我们需要访问ChatGPT的官网&#xff0c;官网提供了很多调用ChatGPT的API接口的语言示例&#xff0c;比如java&#xff0c;go&a…

2023 十大科技趋势发布

达摩院(The Academy for Discovery, Adventure, Momentum and Outlook,Alibaba DAMO Academy),阿里巴巴集团下属研发机构。成立于 2017 年 10 月 11 日,是一所致力于开展基础科学和颠覆式技术创新研究,以人类愿景为驱动力的企业驱动型 " 新型研发机构 ",立足基…

添加万位分隔符

问题:为数值添加万位分隔符 函数公式解决:=TEXT(A1,0&REPT("!,0000",LEN(INT(A1))/4.1)&".00")Len(Int(A1))/4.1,是一种为省字符而取巧的写法,正经写法是(Len(Int(A1))-1)/4,目的是为让结果与位数关联:4位及以内的整数部分为0;5-8位的整数…

设计模式之解释器模式

什么是解释器模式 解释器模式定义了一个语言的文法&#xff0c;并且建立一个解释器来解释语言中的句子。     解释器模式主要包含以下几个角色&#xff1a;         AbstractExpression(抽象表达式)&#xff1a;声明了抽象的解释操作。         TerminalEx…

记录每日LeetCode 1138.字母板上的路径 Java实现

题目描述&#xff1a; 我们从一块字母板上的位置 (0, 0) 出发&#xff0c;该坐标对应的字符为 board[0][0]。 在本题里&#xff0c;字母板为board ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"]&#x…

整型提升+算术转换——“C”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是之前操作符那篇博客中没有讲完的内容&#xff0c;整型提升这个小知识点也非常重要&#xff0c;那现在&#xff0c;就让我们进入操作符的世界吧 隐式类型转换 算术转换 操作符的属性 隐式类型转换 表达式求值的顺序一部…