zoukankan      html  css  js  c++  java
  • 【设计模式(九)】结构型模式之装饰器模式

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

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


    前言

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

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

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

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

  • 相关阅读:
    web常用自动化库——selenium总结(转)
    前端框架面试题
    SpringBoot整合Knife4j展示更美观的API文档
    JUC- ThreadLocal学习笔记
    JUC-ThreadPool线程池的使用与学习
    Java8 新特性
    SpringBoot 整合FreeMarker进行邮件发送
    IDEA 打开别人的项目的是Maevn插件依赖出错问题处理
    Liunx常用指令备查
    第四次作业
  • 原文地址:https://www.cnblogs.com/silent-bug/p/13877606.html
Copyright © 2011-2022 走看看