zoukankan      html  css  js  c++  java
  • 「补课」进行时:设计模式(9)——在请假中使用的责任链模式

    1. 前文汇总

    「补课」进行时:设计模式系列

    2. 请假

    作为一位新时代的社畜,我们每天起得比鸡早,睡得比狗晚,还时不时的要受到上司的 PUA ,每天都生活在水深火热之中。

    生活中总会有各种意外,比如生病了,需要去医院看病,那我们需要请假去医院,一般在公司中,请假的时长和审批领导息息相关,如果这个规则是这样的:

    • 请假 3 天内小组长可以审批
    • 请假 5 天内需要大组长神品
    • 请假 20 天内需要部门经理审批

    如果按照顺序思维来写程序的话,那么我们需要做大量的 if...else 的判断,并且所有的类都要耦合在一起,这时,我们可以使用责任链模式,上面的审批流成如下:

    我们可以先定义一个员工类:

    public interface IPerson {
        // 获取当前请假天数
        int getDays();
        // 获取审批结果
        String getResult();
    }
    
    public class Person implements IPerson {
    
        private int days;
        private String message;
    
        public Person(int days) {
            this.days = days;
            this.message = "领导,我想请 " + days + " 天假!!!";
        }
    
        @Override
        public int getDays() {
            return this.days;
        }
    
        @Override
        public String getResult() {
            return this.message;
        }
    }
    

    创建一个抽象 Handler 类:

    public abstract class Handler {
    
        public final static int MIN = 3;
        public final static int MIDDLE = 5;
        public final static int MAX = 20;
    
        // 当前能处理的级别
        private int days;
        // 责任传递,定义下一个责任人
        private Handler nextHandler;
        // 所有的类都需要定义自己的能处理的请假天数
        public Handler(int days) {
            this.days = days;
        }
    
        public final void handleMessage(IPerson person) {
            if (this.days > person.getDays()) {
                this.response(person);
            } else {
                if (this.nextHandler != null) {
                    this.nextHandler.handleMessage(person);
                } else {
                    System.out.println("员工想要请假 " + this.days + " 天,超过可以审批的最大权限,那就不批了");
                }
            }
        }
    
        public void setNextHandler(Handler nextHandler) {
            this.nextHandler = nextHandler;
        }
    
        protected abstract void response(IPerson person);
    }
    

    这个类中最核心的部分是定义了 nextHandler 下一个责任人,如果当前的员工请假的请求不属于自己的审批范畴,则会将这个请求转发至下一个审批人。

    接下来是三位具体的审批人:

    public class Leader1 extends Handler {
        // 小组长
        public Leader1() {
            super(Handler.MIN);
        }
    
        @Override
        protected void response(IPerson person) {
            System.out.println("-----------向小组长请示------------");
            System.out.println(person.getResult());
            System.out.println("-----------请示通过---------------");
        }
    }
    
    public class Leader2 extends Handler {
        // 大组长
        public Leader2() {
            super(Handler.MIDDLE);
        }
    
        @Override
        protected void response(IPerson person) {
            System.out.println("-----------向大组长请示------------");
            System.out.println(person.getResult());
            System.out.println("-----------请示通过---------------");
        }
    }
    
    public class Leader3 extends Handler {
        // 部门经理
        public Leader3() {
            super(Handler.MAX);
        }
    
        @Override
        protected void response(IPerson person) {
            System.out.println("-----------向部门经理请示--------------");
            System.out.println(person.getResult());
            System.out.println("-----------请示通过---------------");
        }
    }
    

    然后我们来写一个测试类:

    public class Test {
        public static void main(String[] args) {
    
            Random random = new Random();
    
            ArrayList<IPerson> personList = new ArrayList<>();
            for (int i = 0; i < 5; i++) {
                personList.add(new Person(random.nextInt(30)));
            }
    
            Handler leader1 = new Leader1();
            Handler leader2 = new Leader2();
            Handler leader3 = new Leader3();
    
            leader1.setNextHandler(leader2);
            leader2.setNextHandler(leader3);
    
            for (IPerson person: personList) {
                leader1.handleMessage(person);
            }
        }
    }
    

    执行结果如下:

    -----------向部门经理请示--------------
    领导,我想请 17 天假!!!
    -----------请示通过---------------
    员工想要请假 20 天,超过可以审批的最大权限,那就不批了
    -----------向小组长请示------------
    领导,我想请 0 天假!!!
    -----------请示通过---------------
    -----------向部门经理请示--------------
    领导,我想请 11 天假!!!
    -----------请示通过---------------
    -----------向大组长请示------------
    领导,我想请 4 天假!!!
    -----------请示通过---------------
    

    3. 责任链模式

    责任链模式定义如下:

    Avoid coupling the sender of a request to its receiver by giving more thanone object a chance to handle the request.Chain the receiving objects andpass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。)

    责任链模式的重点是在 「链」 上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果,这个链类似于一个单向链表的数据结构,从开始一直向后迭代,知道找不到下一个为止,它的通用类图如下:

    通用代码如下:

    3.1 抽象 Handler

    public abstract class Handler {
        private Handler nextHandler;
    
        // 每个处理者都必须对请求作出处理
        public final Response handleMessage(Request request) {
            Response response = null;
            // 判断当前处理级别
            if (this.getHandlerLevel().equals(request.getLevel())) {
                response = this.echo(request);
            } else {
                // 判断是否有下一个处理者
                if (this.nextHandler != null) {
                    response = this.nextHandler.handleMessage(request);
                } else {
                    // 没有匹配的业务处理者,逻辑根据具体场景实现
                }
            }
            return response;
        }
        // 设置下一个处理者
        public void setNextHandler(Handler nextHandler) {
            this.nextHandler = nextHandler;
        }
    
        protected abstract Level getHandlerLevel();
    
        protected abstract Response echo(Request request);
    }
    

    抽象的处理者实现三个职责:

    • 定义一个请求的处理方法 handleMessage ,唯一对外开放的方法。
    • 定义一个链的编排方法 setNext ,设置下一个处理者。
    • 定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别 getHandlerLevel 和具体的处理任务 echo 。

    3.2 具体处理者

    public class ConcreteHandler1 extends Handler {
        @Override
        protected Level getHandlerLevel() {
            // 设置自己的处理级别
            return null;
        }
    
        @Override
        protected Response echo(Request request) {
            // 设置处理的业务功能
            return null;
        }
    }
    
    public class ConcreteHandler2 extends Handler {
        @Override
        protected Level getHandlerLevel() {
            return null;
        }
    
        @Override
        protected Response echo(Request request) {
            return null;
        }
    }
    
    public class ConcreteHandler3 extends Handler {
        @Override
        protected Level getHandlerLevel() {
            return null;
        }
    
        @Override
        protected Response echo(Request request) {
            return null;
        }
    }
    

    定义三个具体的处理者,以便组成一个链。

    3.3 其余相关代码

    public class Level {
    }
    
    public class Request {
        public Level getLevel() {
            return null;
        }
    }
    
    public class Response {
    }
    

    3.4 测试类

    最后是一个测试类:

    public class Test {
        public static void main(String[] args) {
            Handler handler1 = new ConcreteHandler1();
            Handler handler2 = new ConcreteHandler2();
            Handler handler3 = new ConcreteHandler3();
    
            handler1.setNextHandler(handler2);
            handler2.setNextHandler(handler3);
    
            Response response = handler1.handleMessage(new Request());
        }
    }
    

    在实际的使用过程中,最后都会有一个封装类对责任链模式进行封装,用来取代我们现在的这个测试类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模块的调用,减少模块间的耦合,提高系统的灵活性。

    4. 优点

    任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌(例如在 J2EE 项目开发中,可以剥离出无状态 Bean 由责任链处理),两者解耦,提高系统的灵活性。

    5. 缺点

    责任链有两个非常显著的缺点:

    • 性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。
    • 调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
  • 相关阅读:
    JavaScript单线程和浏览器事件循环简述
    Promise的前世今生和妙用技巧
    自定义Angular插件
    smartcrop.js智能图片裁剪库
    判断是否安装微博
    Java 注解
    android tools使用方式
    listview复用机制研究
    java 驼峰字符和下划线字符相互转换工具类
    剪切板(复制、粘贴)工具类
  • 原文地址:https://www.cnblogs.com/babycomeon/p/13956971.html
Copyright © 2011-2022 走看看