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

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

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


前言

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

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

吃了一口,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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354