美文网首页铲屎官的“设计模式”打通教程
设计模式之零八:“排排坐吃果果的”责任链模式

设计模式之零八:“排排坐吃果果的”责任链模式

作者: c2aa1d94244a | 来源:发表于2018-05-01 16:10 被阅读0次

    阅读本篇大概需要 12 分钟。

    首先,这个系列的文章在微信公众号:皮克啪的铲屎官 上全部都有,欢迎大家关注。

    惯例,先说正事儿:

    每日一皮克啪

    这就是过 五一 的皮克啪↓↓↓↓↓

    五一的皮克啪

    正事儿说完,咱们先来扯几句相关话题:
    所有的设计模式源代码,我均已上传到了GitHub上,欢迎Star,么么哒:

    https://github.com/SwyftG/DesignPatternExample

    接下来,咱们来聊聊 责任链模式
    责任链模式(Chain-of-responsibility Pattern)。Wikipedia解释如下:

    "The chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects.Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain."

    大致意思是:责任链模是由一系列处理事物的类组成的。每一个具体处理事物的类,都有相同的处理方法,只不过他们之间是有级别之分的,当前处理不了,就会交给下一个处理,知道最后一个。

    责任链模式,是有那种一个需要处理的方法,当责任链全部遍历之后,都处理不了的情况的。

    责任链的UML图如下:
    责任链UML

    可以看到,这个里面主要包含这么几个角色:

    • Handler:这个是处理者的抽象类,声明一个处理方法
    • Receiver:是具体的处理类,他们实现共同的方法,如果当前对象不能处理,则会将请求交给下一个对象。

    这么来看,其实责任链模式其实不难,就是一个抽象的处理类,定义处理方法的接口,然后所有具体处理类再依次连城一个链,尝试着挨个处理就好。但是这里需要注意:链表避免首尾相连,造成死循环

    操作实例

    假设PeekPa是一只健康的蓝猫,他的消化食物的能力和吃进去食物的硬度有关系。比如:

    • (这里姑且算食物),硬度为0.0
    • 肉罐头,硬度为2.5
    • 猫粮,一粒一粒的,硬度为5.0
    • 风干牛肉干,硬度为9.9

    PeekPa的消化系统处理时间应该是这样的:

    • 硬度[0.0, 2.0)之间,需要4个小时
    • 硬度[2.0, 6.0)之间,需要8个小时
    • 硬度[6.0, 9.0)之间,需要12个小时
    • 硬度[9.0, 10.0)之间,需要16个小时

    在这样的场景里,我们完全可以使用责任链模式来编写代码。

    首先来先建立吃的:

    // Food抽象基类
    abstract class Food {
        // 食物的硬度
        public double solidity;
        // 食物的名字
        public String name;
    
        public double getSolidity() {
            return solidity;
        }
    }
    
    // 具体食物--水,硬度--0.0
    public class Water extends Food{
        private final Double WATER_SOLIDITY = 0.0;
    
        public Water() {
            this.name = this.getClass().getSimpleName();
            this.solidity = WATER_SOLIDITY;
        }
    }
    
    // 具体食物--猫罐头,硬度--2.5
    public class CatCan extends Food{
        private final Double MEETCAN_SOLIDITY = 2.5;
    
        public CatCan() {
            this.name = this.getClass().getSimpleName();
            this.solidity = MEETCAN_SOLIDITY;
        }
    }
    
    // 具体食物--猫粮,硬度--5.0
    public class CatFood extends Food{
        private final Double CATFOOD_SOLIDITY = 5.0;
    
        public CatFood() {
            this.name = this.getClass().getSimpleName();
            this.solidity = CATFOOD_SOLIDITY;
        }
    }
    
    // 具体食物--风干牛肉干,硬度--9.9
    public class BeefJerky extends Food{
        private final Double BEEFJERKY_SOLIDITY = 9.9;
    
        public BeefJerky() {
            this.name = this.getClass().getSimpleName();
            this.solidity = BEEFJERKY_SOLIDITY;
        }
    }
    
    // 具体食物--砖头,硬度--100.0
    // 这个是用来检测责任链不能处理的话,会是什么情况
    public class Brick extends Food{
        private final Double BRICK_SOLIDITY = 100.0;
    
        public Brick() {
            this.name = this.getClass().getSimpleName();
            this.solidity = BRICK_SOLIDITY;
        }
    }
    

    吃的弄完,下面我们来新建一下PeekPa的消化处理类:
    处理事物的基类DigestionTime

    abstract class DigestionTime {
        // 下一个处理事物的处理类
        protected DigestionTime nextHandler;
    
        protected abstract double getSoftHandleSolidity();
        protected abstract double getHardHandleSolidity();
        // 计算消化时间
        protected abstract void calculateTime(Food food);
        // *关键方法*
        // 当前节点理不了的时候,会交给责任链下一个节点处理
        public final void handleFood(Food food){
            if (food.getSolidity() >= getSoftHandleSolidity() && food.getSolidity() < getHardHandleSolidity()) {
                this.calculateTime(food);
            } else {
                if (nextHandler != null) {
                    nextHandler.handleFood(food);
                } else {
                    System.out.println("PeekPa can't digest: " + food.name);
                }
            }
        }
    }
    

    下面是责任链处理类的具体类:

    // Part I,消化[0.0, 2.0)的食物,需要 4 个小时
    public class PartI extends DigestionTime{
        private final int PART_I_DIGEST_TIME = 4;
        @Override
        protected double getSoftHandleSolidity() {
            return 0.0;
        }
    
        @Override
        protected double getHardHandleSolidity() {
            return 2.0;
        }
    
        @Override
        protected void calculateTime(Food food) {
            System.out.println("This is PartI\n PeekPa needs: " + PART_I_DIGEST_TIME + " hours to digestive " + food.name);
        }
    }
    
    // Part II,消化[2.0, 5.0)的食物,需要 8 个小时
    public class PartII extends DigestionTime{
        private final int PART_II_DIGEST_TIME = 8;
        @Override
        protected double getSoftHandleSolidity() {
            return 2.0;
        }
    
        @Override
        protected double getHardHandleSolidity() {
            return 5.0;
        }
    
        @Override
        protected void calculateTime(Food food) {
            System.out.println("This is PartII\n PeekPa needs: " + PART_II_DIGEST_TIME + " hours to digestive " + food.name);
        }
    }
    
    // Part III,消化[5.0, 9.0)的食物,需要 12 个小时
    public class PartIII extends DigestionTime{
        private final int PART_III_DIGEST_TIME = 12;
        @Override
        protected double getSoftHandleSolidity() {
            return 5.0;
        }
    
        @Override
        protected double getHardHandleSolidity() {
            return 9.0;
        }
    
        @Override
        protected void calculateTime(Food food) {
            System.out.println("This is PartIII\n PeekPa needs: " + PART_III_DIGEST_TIME + " hours to digestive " + food.name);
        }
    }
    
    // Part IV,消化[9.0, 10.0)的食物,需要 16 个小时
    public class PartIV extends DigestionTime{
        private final int PART_IV_DIGEST_TIME = 16;
        @Override
        protected double getSoftHandleSolidity() {
            return 9.0;
        }
    
        @Override
        protected double getHardHandleSolidity() {
            return 10.0;
        }
    
        @Override
        protected void calculateTime(Food food) {
            System.out.println("This is PartIV\n PeekPa needs: " + PART_IV_DIGEST_TIME + " hours to digestive " + food.name);
        }
    }
    

    万事具备,下面我们来封一下PeekPa消化的“责任链” PeekPaDigestiveSystem

    public class PeekPaDigestiveSystem {
        // 责任链中的四个处理节点
        private DigestionTime partI;
        private DigestionTime partII;
        private DigestionTime partIII;
        private DigestionTime partIV;
    
        public PeekPaDigestiveSystem() {
            partI = new PartI();
            partII = new PartII();
            partIII = new PartIII();
            partIV = new PartIV();
    
            // 建立节点之间的关系
            partI.nextHandler = partII;
            partII.nextHandler = partIII;
            partIII.nextHandler = partIV;
        }
    
        public void calculateDigestiveTime(Food food){
            partI.handleFood(food);
        }
    }
    

    OK,现在我们来实践一下,看看我们的责任链好不好用:

        // 声明食物
        Food water = new Water();
        Food catCan = new CatCan();
        Food catFood = new CatFood();
        Food beefJerky = new BeefJerky();
        Food brick = new Brick();
        // 声明责任链
        PeekPaDigestiveSystem system = new PeekPaDigestiveSystem();
    
        // 调用处理方法
        system.calculateDigestiveTime(water); // "This is PartI"
                                              // "PeekPa needs: 4 hours to digestive Water"
        system.calculateDigestiveTime(catCan); // "This is PartII"
                                               // "PeekPa needs: 8 hours to digestive CatCan"
        system.calculateDigestiveTime(catFood); // "This is PartIII"
                                                // "PeekPa needs: 12 hours to digestive CatFood"
        system.calculateDigestiveTime(beefJerky); // "This is PartIV"
                                                  // "PeekPa needs: 16 hours to digestive BeefJerky"
        system.calculateDigestiveTime(brick); // "PeekPa can't digest: Brick"
    

    我们看到,这个计算结果没有问题,当一个食物送到责任链里时,一个一个的节点按照顺序来处理事物。

    责任链模式可以应用在:Log系统,JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter等

    总结一下

    责任链比较简单,将具体处理类通过单链表的形式串联起来。但是由于他的结构比较特殊,在递归调用的时候,需要格外注意。

    优点: 请求者和处理者解耦,结构清晰,代码灵活
    缺点: 处理类特别多,当一个请求复杂的时候,需要遍历责任链,影响性能

    请大家关注一下我的公众号:皮克啪的铲屎官

    qr_code.png

    是一个日更的微信公众号,每天都有新知识,大家一起交流进步。

    相关文章

      网友评论

        本文标题:设计模式之零八:“排排坐吃果果的”责任链模式

        本文链接:https://www.haomeiwen.com/subject/gctfrftx.html