专栏首页开心的学习之路责任链模式(Chain of Responsibility)

责任链模式(Chain of Responsibility)

以此回顾学习《设计模式之禅》的责任链模式。

什么是责任链模式?

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. (为了避免请求者与响应者之间的耦合性,让每个对象都有处理请求的机会。把这些处理请求的对象连成一条链,并沿着这条链传递请求,直到有对象能够处理这个请求为止)

比如失物招领处发出一个认领失物的请求,然后甲乙丙丁都来认领,就把甲乙丙丁串成一条责任链,甲先看看是不是他的物品,不是就传给乙,乙再看看是不是自己的,不是再传给丙,以此类推,直到失物被认领。if...else if....else if....else其实就是责任链模式的小体现。

用责任链模式有什么好处?

像定义中说的,将请求和处理分开,两者解耦,请求者可以不用知道是谁处理的,处理者也可以不用知道是谁请求的。相比if...else if....else,灵活性高一些,耦合度低一些,可读性高一些。

责任链模式有什么缺点?

和if...else一样,责任链模式在找到请求处理者之前,要把链表遍历一遍,如果责任链很长,效率就会低。

什么时候使用责任链模式?

(1)当代码中有很多if..else语句,并且严重影响了可读性的时候,可以考虑重构到责任链模式

(2)如果代码需要添加新的处理请求的类的概率高且频繁,可以考虑责任链模式,责任链模式的灵活性高

如何使用责任链模式?

UML类图:

1、定义一个处理请求的Handler类,可以是抽象类,也可以是接口,一般里面有两个方法(接口),一个是setNext(),用来指定责任链的下一个节点,一个是handle(),用来实现对请求的处理。

2、定义ConcreteHandler类,继承Handler抽象类(实现Handler接口),并实现setNext()和handle()方法。

3、定义场景类(Client),设置ConcreteHandler的顺序,调用第一个ConcreteHandler的handle()方法。

例如,输入两个数,给一个命令(add/sub/mult/div),进行加减乘除运算,用责任链模式实现如下:

UML图

public interface Chain {
    void setNextChain(Chain nextChain);
    void calculate(Numbers request);
}

Numbers类

public class Numbers {
    private int number1;
    private int number2;
    private String calculateWanted;

    public Numbers(int number1, int number2, String calculateWanted) {
        this.number1 = number1;
        this.number2 = number2;
        this.calculateWanted = calculateWanted; 
    }  

    public int getNumber1() {
        return number1;
    }

    public int getNumber2() {
        return number2;
    }

    public String getCalculateWanted() {
        return calculateWanted;
    }

}

AddNumbers类:

public class AddNumbers implements Chain {
    private Chain nextChain;
    
    @Override
    public void setNextChain(Chain nextChain) {
        this.nextChain = nextChain;
    } 

    @Override
    public void calculate(Numbers request) {
        if (request.getCalculateWanted().equals("add")) {
            System.out.println(request.getNumber1() + "+" + request.getNumber2() + "=" (request.getNumber1() + request.getNumber2()));
        } else {
            nextChain.calculate(request);
        }
    }
}

SubNumbers、MultNumbers、DivNumbers也痛AddNumbers结构一样,就不再写了,只是我们吧DivNumbers当作责任链尾,它没有后继节点,所以在DivNumbers中的else语句中输出“Only works for add、sub、mult、div“。

Client类:

public class Client {
    public static void main(String[] args) {
        Chain chainCalc1 = new AddNumbers();
        Chain chainCalc2 = new SubNumbers();
        Chain chainCalc3 = new MultNumbers();
        Chain chainCalc4 = new DivNumbers();  
  
        chainCalc1.setNextChain(chainCalc2);
        chainCalc2.setNextChain(chainCalc3);
        chainCalc3.setNextChain(chainCalc4);
        Numbers request = new Numbers(4, 2, "add");
        chainCalc1.calculate(request);
    }
}

运行结果为:4 + 2 = 6

使用责任链模式有哪些注意事项?

责任链中的节点数量要控制,避免超长链影响性能和调试,一般做法是在Handler中设置一个最大节点数量,在setNext中判断是否已超过阈值,超过则不允许该链建立,不过我觉得还是人为控制节点数量比较好,毕竟多数情况下是不会让链超长,每次遍历都要判断一次是否超长,有些浪费。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 机器学习项目流程及模型评估验证

    4.9日到现在一直在做Udacity的P1项目——波士顿房价预测。这个项目让我收获最大的就是理清了机器学习解决问题的整体流程,搭起一个框架,学会了寻找模型的最优...

    刘开心_1266679
  • 基础练习 数列排序

      第一行为一个整数n。   第二行包含n个整数,为待排序的数,每个整数的绝对值小于10000。

    刘开心_1266679
  • 基础练习 Sine之舞

      最近FJ为他的奶牛们开设了数学分析课,FJ知道若要学好这门课,必须有一个好的三角函数基本功。所以他准备和奶牛们做一个“Sine之舞”的游戏,寓教于乐,提高奶...

    刘开心_1266679
  • 机器学习之随机森林

    随机森林(Random Forest)是一个非常灵活的机器学习方法,从市场营销到医疗保险有着众多的应用。例如用于市场营销对客户获取和存留建模或预测病人的疾病风险...

    小一
  • 爬虫基本原理完全梳理及常用解析方式

    什么是爬虫:即网络爬虫,可以理解为在网络上爬行的一只蜘蛛,互联网可以比喻为一张大网,一只蜘蛛在爬行时遇到了所需的资源就可以把它爬取下来。简单来说,爬虫就是请求网...

    企鹅号小编
  • 设计模式之命令模式-引导篇及原理

    项目背景:随着物联网的流行以及智能化家具普及。一个遥控器可以管控家里所有设备(家电等)也很正常了。

    凯哥Java
  • 重构 - 完全不用 if-else 可能吗?

    上次那篇重构-为什么 if-else 不是好代码 说到代码中的 if-else会随着代码量的增加,在迭代的过程中变的越来越难以维护, 然后用工厂模式的思路可以把...

    PhoenixZheng
  • AMD and CMD are dead之js模块化黑魔法

    缘由 在2013-03-06 13:58的时候,曾甩下一片文章叫:《为什么不使用requirejs和seajs》,并放下豪言说发布一款完美的模块化库,再后来就把...

    IMWeb前端团队
  • C#时间与时间戳格式互相转化

    C#时间格式转换为时间戳(互转) 时间戳定义为从格林威治时间 1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至...

    风骨散人Chiam
  • 如何有效的学习-费曼学习法

    理查德·菲利普斯·费曼,高中毕业之后进入麻省理工学院学习,最初主修数学和电力工程,后转修物理学。1939年以优异成绩毕业于麻省理工学院,1942年6月获得普林斯...

    用户1278550

扫码关注云+社区

领取腾讯云代金券