设计模式(11):装饰模式

尺有所短,寸有所长;不忘初心,方得始终。

一、装饰模式是什么

装饰模式又名包装(Wrapper)模式,是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。

比如最近深圳降温了,早上起来贼冷,我果断的加了一件衣服,此时我还是我自己没变,但是又比之前多了一件衣服这个保暖的功能,这就是装饰模式

设计模式(11):装饰模式
  • 【组合代替继承】

    一般情况下,会使用继承方式来扩展一个类的功能。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。

    装饰器模式是使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能。

    装饰器模式实际上是继承关系的一种组合代替继承的替代方案。所以我们在理解装饰模式之前需要先理解什么是组合以及为什么要用组合替代继承

    Java的面向对象中有继承与封装的特性,继承表示子类依赖了父类中的实现,而父类的内部细节对于子类是可见的,所以继承常常被认为破坏了封装性,相互矛盾。而组合就是利用封装的特性来实现继承功能

  • 【主要作用】

    动态地给一个对象添加一些额外的职责。这一点装饰器模式相比生成子类更为灵活。

    一般我们使用继承扩展一个类时,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

二、装饰模式的适用场景

  • 确保业务逻辑可用一个基本组件及多个额外可选层次表示

  • 扩展一个类的功能或给一个类增加附加责任,又不想增加很多子类。

    例如,我们在使用IDEA的开发的时候经常会添加很多插件,这些插件因人而异,每个人都有可能不一样

  • 给一个对象增加由一些基本功能的排列组合而产生的非常大量的功能

  • 动态地给一个对象增加功能,这些功能可以再动态地撤销。

在Java中 FileReader 增加缓冲区而采用的装饰类 BufferedReader ,使得FileReader 有readLine方法

BufferedReader in = new BufferedReader(new FileReader("文件地址"));
String s = in.readLine();

三、装饰模式结构

  • 抽象构件(Component)角色:定义一个抽象接口,声明封装器和被封装对象的公用接口。
  • 具体构件(ConcreteComponent)角色:实现抽象构件,定义了基础行为, 装饰类可以改变这些行为。
  • 抽象装饰(Decorator)角色:继承抽象构件,并持有一个抽象构件对象实例的成员变量,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并调用父类方法之前或之后给具体构件对象添加附加的行为。
  • 客户端 (Client) 可以使用多层装饰来封装部件, 只要它能使用通用接口与所有对象互动即可。设计模式(11):装饰模式

四、装饰模式实现方式

  • 找出基本组件和抽象装饰角色的通用方法,定义一个抽象构件(Component)接口并声明这些方法。

  • 创建一个具体构件(ConcreteComponent)类, 并定义其基础行为。

  • 创建抽象装饰(Decorator)类,继承抽象构件,使用一个抽象构件对象实例的成员变量存储指向被封装对象的引用。

    成员变量必须被声明为组件接口类型, 使得能在运行时连接具体组件和装饰。装饰基类必须将所有工作委派给被封装的对象

  • 声明具体装饰(ConcreteDecorator),实现抽象装饰类,具体装饰必须在调用父类方法之前或之后执行自身的行为。

  • 客户端负责创建装饰并将其组合成客户端所需的形式。

五、装饰模式的实现

【案例】:天气冷了,打工人给自自己加件衣服去上班。

【案例分析】:上班是通用行为方法(打工人的宿命…….),加衣服是装饰行为方法

  • 抽象构件(Component)角色

    /**
    * 抽象构件(Component)角色:打工人
    */

    public interface HitWorker {
    /**
    * 打工人的通用行为:上班
    */

    public void work();
    }
  • 具体构件(ConcreteComponent)角色

    /**
    * 具体构件(ConcreteComponent)角色 张三
    */

    public class ZhangSan implements HitWorker{
    @Override
    public void work() {
    System.out.println("张三去上班搬砖了......");
    }
    }
  • 抽象装饰(Decorator)角色

    /**
    * 抽象装饰(Decorator)角色:
    */

    public class Decorator implements HitWorker{

    /**
    * 抽象构件对象实例的成员变量存储指向被封装对象的引用
    */

    private HitWorker hitWorker;

    public Decorator(HitWorker hitWorker) {
    this.hitWorker = hitWorker;
    }

    @Override
    public void work() {
    hitWorker.work();
    }
    }
  • 具体装饰(ConcreteDecorator)角色

    /**
    * 具体装饰(ConcreteDecorator)角色:毛衣
    */

    public class SweaterDecorator extends Decorator {

    public SweaterDecorator(HitWorker hitWorker) {
    super(hitWorker);
    }

    /**
    * 具体装饰必须在调用父类方法之前或之后执行自身的行为。
    */

    @Override
    public void work() {
    System.out.println("具体装饰在调用父类方法之前执行自身的行为");
    System.out.println("张三穿上了毛衣");
    super.work();
    System.out.println("具体装饰在调用父类方法之后执行自身的行为");
    }
    }
  • 客户端代码实现

      public static void main(String[] args) throws Exception {
    /**
    * 客户端负责创建装饰
    */

    HitWorker zhangSan = new ZhangSan();
    /**
    * 并将其组合成客户端所需的形式
    */

    Decorator decorator = new SweaterDecorator(zhangSan);
    decorator.work();
    }
  • 案例输出结果

设计模式(11):装饰模式

六、装饰模式的优缺点

优点

  • 装饰器是继承的替代方案,比继承灵活,在不改变原有对象的情况下动态的给一个对象扩展功能,即插即用。
  • 通过使用不同装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则

缺点

装饰器模式会增加许多子类,过度使用会增加程序得复杂性。

七、装饰模式和其他模式的区别

  • 组合模式装饰的结构图相似, 两者都依赖递归组合来组织无限数量的对象。可以使用装饰来扩展组合树中特定对象的行为。

    装饰类似于组合, 但其只有一个子组件。

    装饰为被封装对象添加了额外的职责, 组合是对其子节点的结果进行了 求和

  • 装饰模式可让给一个对象扩展功能,改变对象的外表。策略模式则是在本质上改变对象的实现。

  • 装饰模式代理模式结构相似,都是一个对象将部分工作委派给另一个对象。

    代理通常自行管理其服务对象的生命周期, 装饰的生成则总是由客户端进行控制。

八、总结

装饰者模式实际上类似AOP思想的一种实现方式,跟AOP类似的它可以控制被装饰者行为,在调用父类方法之前或之后给被装饰者添加附加的行为。

原文始发于微信公众号(星河之码):设计模式(11):装饰模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/27152.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!