【设计模式(九)】结构型模式之装饰器模式

个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道

如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充


前言

中秋刚过没多久,虽然我这种粗人对月饼无感,但是公司发的肯定得收的嘛

拿回家当零食吃算了,一个硬纸袋,打开是一个盒子,盒子打开时十多个小盒子,小盒子打开是塑料包装的月饼,撕开塑料包装,终于能吃了

吃了一口,emmm就这?还没小时候吃的冰糖五仁月饼好吃呢,弄这么花里胡哨

就一块味道并不咋地的月饼,花里胡哨包装一层又一层,何必呢?还不如改善工艺做好吃点

说的再难听点,这些精心包装的月饼,跟普通袋装的月饼,区别也就只是包装吧(虽然大部分人送的是心意)

包装对于月饼,就只是装饰而已,差不多做法生产的月饼,经过一层又一层包装,就能变成在店铺里形形色色的礼品月饼,本质上依然是月饼

而且理论上包装可以无限层的套娃。。。。


我们早上去早餐店买煎饼,可以加鸡蛋、生菜、肉松等等,从一个薄饼整成一个巨无霸,但说到底,仍然是个煎饼。


装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

添加了新功能后是原对象的子类,根据里氏代换原则,新的对象也能够被再次修饰,进而可以无限套娃


1.介绍

使用目的:向一个现有的对象添加新的功能,同时又不改变其结构

使用时机:在不想改变结构的情况下扩展类

解决问题:避免扩展时常规继承方法,随着扩展功能的增多,子类会很膨胀

实现方法Component 类充当抽象角色,修饰类引用和继承 Component 类,具体扩展类重写父类方法,而修饰类也同样可被扩展

应用实例:

  • 买手抓饼的时候,我们并非只有做之前可以说加什么,做完之后加肉松,或者加火腿什么的都是可以的,但怎么加都是手抓饼
  • 化妆的时候,经常是涂涂抹抹好几层,而且顺序并不严格固定,也可以最后根据情况再涂几层,怎么好看怎么来,但怎么涂都是自己的脸

优点

  1. 装饰类和被装饰类可以独立发展,不会相互耦合
  2. 可以被多次装饰,使用时自由度高

缺点:多层装饰时思路比较复杂,需要清晰的理解业务


2.结构

通常包括4个角色

  • 抽象组件角色(Component): 定义可以动态添加任务的对象的接口
  • 具体组件角色(ConcreteComponent):定义一个要被装饰器装饰的对象,即 Component 的具体实现
  • 抽象装饰器(Decorator): 维护对组件对象和其子类组件的引用,需要实现 Component
  • 具体装饰器角色(ConcreteDecorator):向组件添加新的职责

image-20201026091625651

  • ConcreteComponent负责Component的具体实现
  • Decorator是一个虚拟类,需要实现Component,并持有一个Component对象
  • ConcreteDecorator作为装饰器,负责给组件添加职责

ConcreteDecorator作为Decorator的子类,根据里氏代换原则,也可被ConcreteDecorator持有并装饰

说白了就是,我装饰我自己


3.实现

  1. 定义抽象组件角色(Component)

    interface Component {
        String operate();
    }
    
  2. 定义具体组件角色(ConcreteComponent),并实现Component

    class ConcreteComponent implements Component {
    
        @Override
        public String operate() {
            return "原始对象";
        }
    }
    
  3. 定义抽象装饰器(Decorator),实现Component

    abstract class Decorator implements Component {
        private Component component;
    
        public Decorator(Component component) {
            this.component = component;
        }
    
        @Override
        public String operate() {
            //调用被装饰者的方法
            return component.operate();
        }
    }
    
  4. 定义具体装饰器角色(ConcreteDecorator),继承Component

    class ConcreteDecoratorA extends Decorator {
    
        public ConcreteDecoratorA(Component component) {
            super(component);
        }
    
        @Override
        public String operate() {
            return super.operate() + " 加上修饰器A";
        }
    }
    
    class ConcreteDecoratorB extends Decorator {
    
        public ConcreteDecoratorB(Component component) {
            super(component);
        }
    
        @Override
        public String operate() {
            return super.operate() + " 加上修饰器B";
        }
    }
    
  5. 测试客户端

    public class DecoratorTest {
        public static void main(String[] args) {
            Component component = new ConcreteComponent();
            Component componentA=new ConcreteDecoratorA(component);
            Component componentAB=new ConcreteDecoratorB(componentA);
            Component componentABA=new ConcreteDecoratorA(componentAB);
    
            Component componentBAB=new ConcreteDecoratorB(new ConcreteDecoratorA(new ConcreteDecoratorB(new ConcreteComponent())));
    
            System.out.println(componentABA.operate());
            System.out.println(componentBAB.operate());
    
        }
    

完整代码

package com.company.test.decorator;

interface Component {
    String operate();
}

class ConcreteComponent implements Component {

    @Override
    public String operate() {
        return "原始对象";
    }
}

abstract class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public String operate() {
        //调用被装饰者的方法
        return component.operate();
    }
}

class ConcreteDecoratorA extends Decorator {

    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public String operate() {
        return super.operate() + " 加上修饰器A";
    }
}

class ConcreteDecoratorB extends Decorator {

    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    @Override
    public String operate() {
        return super.operate() + " 加上修饰器B";
    }
}

public class DecoratorTest {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Component componentA=new ConcreteDecoratorA(component);
        Component componentAB=new ConcreteDecoratorB(componentA);
        Component componentABA=new ConcreteDecoratorA(componentAB);

        Component componentBAB=new ConcreteDecoratorB(new ConcreteDecoratorA(new ConcreteDecoratorB(new ConcreteComponent())));

        System.out.println(componentABA.operate());
        System.out.println(componentBAB.operate());

    }
}

运行结果

image-20201026104004926


4.小结

4.1.为何要使用装饰模式

  1. 解耦ConcreteComponentConcreteDecorator,从常见的继承关系变为关联关系,使他们可以独立变化,只需要遵守共同的规则(Component)即可

    否则只能使他们一层一层继承,耦合度极高,不利于扩展

  2. ConcreteComponentConcreteDecorator都算是Component的实现,因此对于Component适用的接口,ConcreteComponentConcreteDecorator都适用,这样以来,客户端使用和外部接口对接也会简单很多

    因此外部代码只需要针对Component设计即可

  3. 可以自己装饰自己,是独一无二的优点,可以无限制的自由增加功能

4.2.存在的隐患

均为设计或者管理不当导致,也就是人为错误,可以凭借开发者能力规避的东西

  1. 套娃次数太多,会导致逻辑混乱

    比如server层之间的互相引用,很容易出现套娃,逻辑极其混乱

  2. 死循环

    类似于递归的死循环爆栈

  3. 装载顺序报错(先有鸡还是先有蛋?)

    比如两个service(A和B)互相引用,那么装载A时需要先装载B,装载B时需要先装载A,会直接报错


后记

我装饰我自己,这就是装饰模式所想达到的目的

装饰后的对象,依然是同一种对象,因此可以非常自由的反复装饰,同时原本适用的接口或者方案,装饰之后依然能够使用

比如,“我是一个人”,加上装饰“我是一个正直善良聪明可爱但也许并不算帅气的男生”,但是说到底我仍然是个人,我拥有一个人所拥有的思考和行为方式


作者:Echo_Ye

WX:Echo_YeZ

Email :echo_yezi@qq.com

个人站点:在搭了在搭了。。。(右键 - 新建文件夹)

原文地址:https://www.cnblogs.com/silent-bug/p/13877606.html