• 链式设计模式——装饰模式和职责链模式


    链式设计模式:

    在23种设计模式中,有一种设计模式实现起来就好像锁链一样,一环扣一环的进行,我通称为链式设计模式。
    今天我们来看看装饰模式职责链模式的逻辑和具体实现。


    装饰模式:

    概述:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活。

    类图:
    装饰模式类图

    • Component :可以是抽象职责类或者接口,给实现的对象动态的添加职责
    • ConcreteComponent :是定义了一个具体的对象,可以给这个对象添加一些职责
    • Decorator :装饰抽象类,继承了Component,从外部类来扩展Component类的功能,但对于Component来说,无需知道Decorator的存在的。
    • ConcerteDecorator :就是具体的装饰对象,起到给Component添加职责的功能。

    注意:如果只有一个ConcrereComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类,同样的道理,如果只有一个ConcreteDecorator类,那么就没必要单独建立一个单独的Decorator类,而可以把Decorator类和ConcreteDecorator的责任合并为一个类。

    例子:一个人的人物装扮
    代码实现:
    人类:被装饰类,最高父类相当于ConcreteComponent

    public class Person {
        private String name;
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        public void show() {
            System.out.println("装扮的"+ name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    服饰类:抽象装饰类,相当于Decorator

    public abstract class Finery extends Person {
        //人对象作为属性
        private Person component;
        
        //get和set方法
        public Person getComponent() {
            return component;
        }
        public void setComponent(Person component) {
            this.component = component;
        }
    
    	//重写父类的某个功能
        public void show(){
            if (component != null){
                component.show();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    具体服饰类:继承抽象服饰类,给人添加职责。相当于ConcerteDecorator

    public class Suit extends Finery {
        public void show() {
            System.out.print("西装  ");
            super.show();
        }
    }
    
    public class TShirts extends Finery {
        public void show(){
            System.out.print("大T恤  ");
            super.show();
        }
    }
    
    .
    .
    .
    
    public class GymShoes extends Finery {
        public void show() {
            System.out.print("球鞋  ");
            super.show();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试类:

    public class test {
        public static void main(String[] args) {
            Person person = new Person("某某");
    
            System.out.print("第一种装扮:");
    
            Finery ts = new TShirts();
            Finery bt = new BigTrouser();
            Finery gs = new GymShoes();
    
            ts.setComponent(person);
            bt.setComponent(ts);
            gs.setComponent(bt);
            gs.show();
    
            System.out.print("第二种装扮:");
    
            Finery ne = new Necktie();
            Finery su = new Suit();
            Finery ls = new LeatherShoes();
    
            ne.setComponent(person);
            su.setComponent(ne);
            ls.setComponent(su);
            ls.show();
    
        }
    }
    
    运行结果:
    第一种装扮:球鞋  垮裤  大T恤  装扮的某某
    第二种装扮:皮鞋  西装  领带  装扮的某某
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    我们看一下测试的代码:

    		Person person = new Person("某某");
    		Finery ts = new TShirts();
            Finery bt = new BigTrouser();
            Finery gs = new GymShoes();
    
            ts.setComponent(person);
            bt.setComponent(ts);
            gs.setComponent(bt);
            gs.show();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    装饰的方法是:首先用person实例化对象person,然后用TShirts的实例化对象ts包装person,再用BigTrouser实例化对象包装ts,最后再用GymShoes实例化对象包装bt,再调用gs的show()方法。

    了解这个过程我们就可以知道,装饰模式是利用SetComponent来对对象进行包装的,这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中
    运行过程就像打开一个俄罗斯套娃一样,一层套一层,当你打开时,最先展示的就是最外层的语句。所以第一套装扮是球鞋在先,大T恤在后。

    总结:装饰模式是为已有的功能动态的添加更多的功能的一种方式
    那什么时候适合去使用它呢?
    正常不使用装饰模式下: 当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为,这么做的问题主要是在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅只是为了满足一些只在特定情况下才会执行的特殊行为的需要。
    在使用装饰模式下: 它是把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选则的按顺序使用装饰功能包装对象。

    装饰模式的优点:
    1.把类中的装饰功能从类中搬移出去,简化了原有类
    2.有效的把类的核心职责和装饰功能分开了,而且可以去除相关类中重复的装饰逻辑。


    职责链模式

    概述:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止

    职责链模式类图

    • Client :请求类,包含请求内容等等
    • Handler :抽象处理请求类:将自身设为自己的属性,再定义一个解决请求的抽象方法
    • ConcreteHandler : 具体处理请求类,继承Handler抽象类,重写解决请求的方法。每一个的解决请求的方法体可以相互包含,也可以形成互补的方法。可访问它的后继者,如果可处理该请求,就处理。否则就将该请求转发给它的后继者。

    例子:在公司请假
    代码实现:
    请求类:包含请求内容等等,相当于Client

    public class Request {
        private String requestType;//类别
        private String requestContent;//内容
        private int number;//数量
    
        public Request() {
        }
    
        public String getRequestType() {
            return requestType;
        }
        
        public void setRequestType(String requestType) {
            this.requestType = requestType;
        }
        
        public String getRequestContent() {
            return requestContent;
        }
        
        public void setRequestContent(String requestContent) {
            this.requestContent = requestContent;
        }
        
        public int getNumber() {
            return number;
        }
        
        public void setNumber(int number) {
            this.number = number;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    抽象处理请求类:将自身设为自己的属性,再定义一个解决请求的抽象方法,相当于Handler

    public abstract class Manager {
        protected String name;
        protected Manager superior;
    
        public Manager(String name) {
            this.name = name;
        }
        
        //get和set方法
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public Manager getSuperior() {
            return superior;
        }
        
        public void setSuperior(Manager superior) {
            this.superior = superior;
        }
    
    	//抽象解决请求方法
        public abstract void processRequest(Request request);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    具体的处理请求类:继承Handler抽象类,重写解决请求的方法。相当于ConcreteHandler

    public class TechnicalManager extends Manager{
    
        public TechnicalManager(String name) {
            super(name);
        }
    
        @Override
        public void processRequest(Request request) {
            if (request.getRequestType().equals("请假") && request.getNumber() <= 2) {
                System.out.println(name +":"+ request.getRequestContent()+"数量"+request.getNumber()+"被批准");
            }else {
                if (request != null){
                    superior.processRequest(request);
                }
            }
        }
    }
    
    public class Majordomo extends Manager {
    
        public Majordomo(String name) {
            super(name);
        }
    
        @Override
        public void processRequest(Request request) {
            if (request.getRequestType().equals("请假") && request.getNumber() <= 5){
                System.out.println(name +":"+ request.getRequestContent()+"数量"+request.getNumber()+"被批准");
            }else {
                if (request != null){
                    superior.processRequest(request);
                }
            }
        }
    }
    
    public class GeneralManager extends Manager {
    
        public GeneralManager(String name) {
            super(name);
        }
    
        @Override
        public void processRequest(Request request) {
            if (request.getRequestType().equals("请假")){
                System.out.println(name +":"+ request.getRequestContent()+"数量"+request.getNumber()+"被批准");
            }else if (request.getRequestType().equals("涨工资") && request.getNumber() <=500){
                System.out.println(name +":"+ request.getRequestContent()+"数量"+request.getNumber()+"被批准");
            }else if (request.getRequestType().equals("涨工资") && request.getNumber() >500){
                System.out.println(name +":"+ request.getRequestContent()+"数量"+request.getNumber()+"再说吧");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    测试类:

    public class test {
        public static void main(String[] args) {
    
            TechnicalManager tm = new TechnicalManager("aaa");
            Majordomo md = new Majordomo("bbb");
            GeneralManager gm = new GeneralManager("ccc");
    
            tm.setSuperior(md);
            md.setSuperior(gm);
    
            Request request = new Request();
            request.setRequestType("请假");
            request.setRequestContent("ddd因为xxxx想要请假");
            request.setNumber(4);
    
            tm.processRequest(request);
    
    
            Request request1 = new Request();
            request1.setRequestType("涨工资");
            request1.setRequestContent("ddd因为工资太低,想要涨点工资");
            request1.setNumber(1200);
    
            tm.processRequest(request1);
        }
    }
    
    运行结果:
    bbb:ddd因为xxxx想要请假数量4被批准
    ccc:ddd因为工资太低,想要涨点工资数量1200再说吧
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    让我们再看一下代码:

    		TechnicalManager tm = new TechnicalManager("aaa");
            Majordomo md = new Majordomo("bbb");
            GeneralManager gm = new GeneralManager("ccc");
    
            tm.setSuperior(md);
            md.setSuperior(gm);
    
            Request request = new Request();
            request.setRequestType("请假");//原因
            request.setRequestContent("ddd因为xxxx想要请假");//请假内容
            request.setNumber(4);//请假天数
    
            tm.processRequest(request);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    职责链的方法是:首先实例化TechnicalManager、Majordomo、GeneralManager对象,然后用TechnicalManager的实例化对象tm连接Majordomo的实例化对象md,再用Majordomo的实例化对象md连接GeneralManager实例化对象gm,最后再实例化Request对象request。再调用tm的processRequest()方法把request参数传递进去。

    了解这个过程我们就可以知道,职责链模式当客户提交一个请求时,请求是沿着链传递直至有一个ConcreteHandler对象负责处理它
    运行过程就像一根锁链一样,一层连着一层,当你运行时,从最开始的锁链开始,一直传递下去,直到请求被解决。

    总结:职责链模式是请求者不用管哪个对象来处理,反正该请求会被处理就对了

    职责链模式的优点:
    1.接收者和发送者都没有对方明确信息,且链中的对象自己也不知道链的结构,结果是职责链可简化对象的相互连接,它们仅需要保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。大大降低了耦合度。
    2.可以随时增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性

    职责链模式的缺点:
    1.一个请求极有可能到了链的末端都得不到处理,或者因为没有正确的配置而得不到处理。所以要事先考虑清楚。


    装饰模式和职责链模式的异同

    相同点:实现方式都一样,都是将其自身抽象类或抽象父类作为自己的属性,用于达到链式结构。

    不同点:
    当两者的执行逻辑顺序修改是否有差别?(链式结构的顺序,例如:原本a—>b—>c ,现a—>c—>b)
    当装饰模式修改执行逻辑顺序时,必会对结果产生影响。它会按照链式顺序依次执行,如果为a—>b—>c,它会先执行a再执行b再执行c,同样a—>c—>b,那就会先执行a再执行c再执行b。当修改执行逻辑顺序后就一定会对结果产生影响。
    当职责链模式修改执行逻辑顺序时,却不一定产生影响,主要看具体处理请求类中解决请求的代码逻辑是相互包含,还是形成互补的方法。(相互包含就是上面例子写的,a只能决定两天以下的请假处理,b能决定5天以下的请假处理,c可以做任何天数的请假,并可以少量提薪。互补关系就是,a只能做1到5的事,b能做6到10的事,c能做11到20的事)。当具体处理请求类中解决请求的代码逻辑是相互包含时,修改执行逻辑顺序,也可能会改变结果。但是当具体处理请求类中解决请求的代码逻辑是互补的,那么修改执行逻辑顺序,一定不会对结果产生影响。

  • 相关阅读:
    [暑假]Vue框架里面 一些属性和配置项的作用
    windows 驱动与内核调试 学习3
    手写一个埋点SDK吧~
    JNI动态注册以及JNI签名
    2023 OPPO开发者大会正式开幕,ColorOS 14亮相,手机流畅进入「最后一公里」比拼
    linux篇【7】:进程程序替换
    virtualbox7_0 ubunt20_04 共享文件且自动加载
    LeetCode 619, 58, 24
    Win32 - 窗口
    Day112.尚医通:手机验证码登录功能
  • 原文地址:https://blog.csdn.net/m0_63217468/article/details/127789603