23种设计模式(三)--装饰器模式

简介: 23种设计模式(三)--装饰器模式

装饰器模式


一. 什么是装饰器模式?


我们都知道装饰, 元旦, 圣诞节, 我们都需要装饰, 渲染节日气氛. . 所谓装饰, 就是在原来的基础上加东西.


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


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


我们通过下面的实例来演示装饰器模式的用法。其中,我们将把圣诞节的房间在原来的基础上装饰上了气球。


我们来看看UML图:


最开始有房子, 房子有平房, 楼房, 别墅, 我们会打扫房间.


/**
 * 房子抽象类
 */
public interface IHouse {
    void clean();
}
/**
 * 平房
 */
public class Bungalow implements IHouse{
    @Override
    public void clean() {
        // 打扫房间
    }
}
/**
 * 楼房
 */
public class HighRiseBuilding implements IHouse{
    @Override
    public void clean() {
        // 打扫房间
    }
}
/**
 * 别墅
 */
public class Villa implements IHouse{
    @Override
    public void clean() {
        // 打扫房间
    }
}


这时, 遇到节假日, 比如:圣诞节, 元旦, 我们会装饰房间, 给房间布置气球等.


我们可以采用装饰器设计模式. 这时, 装饰的主体依然房子. 装饰就像是一个壳子, 套在房子的外面.

8001f57d23a4405ca39e9c697d25f6ff_tplv-k3u1fbpfcp-zoom-1.png

/**
 * 房子抽象类
 */
public interface IHouse {
    void clean();
}
/**
 * 平房
 */
public class Bungalow implements IHouse{
    @Override
    public void clean() {
        // 打扫房间
        System.out.println("我是平房: 打扫房间");
    }
}
/**
 * 楼房
 */
public class HighRiseBuilding implements IHouse{
    @Override
    public void clean() {
        // 打扫房间
        System.out.println("我是楼房: 打扫房间");
    }
}
/**
 * 别墅
 */
public class Villa implements IHouse{
    @Override
    public void clean() {
        // 打扫房间
        System.out.println("我是别墅: 打扫房间");
    }
}
/**
 * 房间装饰器
 */
public abstract class DecoratorHouse implements IHouse {
    // 被装饰的房子
    protected IHouse decoratedHouse;
    public DecoratorHouse(IHouse decoratedHouse) {
        this.decoratedHouse = decoratedHouse;
    }
}
public class BalloonDecoratorHouse extends DecoratorHouse {
    public BalloonDecoratorHouse(IHouse decoratedHouse) {
        super(decoratedHouse);
    }
    @Override
    public void clean() {
        // 打扫房间
        super.decoratedHouse.clean();
        ballonDecorate();
    }
    public void ballonDecorate() {
        // 用气球装饰房间
        System.out.println("用气球装饰房间, 好漂亮");
    }
}
public class DecoratorPatternTest {
    public static void main(String[] args) {
        IHouse house = new Villa();
        house.clean();
        System.out.println("-------圣诞节到了, 装饰房间-------");
        IHouse decoratorHouse = new BalloonDecoratorHouse(new Villa());
        decoratorHouse.clean();
    }
}

使用装饰器模式以后, 我们在不改变原有房子的基础上进行了扩展. 节假日, 就可以使用装饰类装饰房间, 如果节日过去了, 我们就继续使用原来的房子.


接下来, 我们来总结一些上面的案例.


首先, 我们有一个组件Component, 在这个组件里有一些自定义的功能. 通常这个组件Component是抽象的(接口或者抽象类)


然后, 抽象类会有一个具体的实现 ConcreteComponent, 除了实现组件Component的方法, 还可自定义方法.


第三, 我们现在想要对具体实现ConcreteComponent进行包装, 定义一个包装类

DecoratorComponent. 通常这个包装类是抽象的. 包装类也实现组件Component接口, 然后引入一个Component的具体的成员变量. 为什么要引入成员变量呢? 这个也很好理解, 我们的目标是包装的具体类.


第四, 定义一个具体的装饰器 DecoratorComponent, 再具体的装饰器中, 可以增加额外的方法. 比如在之前后者之后增加一些逻辑. 具体关系图如下:

29eb911aa0cb4f3c9470fcc3ad2304f0_tplv-k3u1fbpfcp-zoom-1.png


代码实现如下:

/**
 * 抽象功能
 */
public interface Component {
    void operate();
}
/**
 * 具体功能实现
 */
public class ConcreteComponent implements Component{
    @Override
    public void operate() {
        // 实现具体的逻辑
        System.out.println("具体实现的逻辑");
    }
}
/**
 * 用来装饰Component对象的类
 */
public abstract class DeceratorComponent implements Component{
    // 指定装饰的对象
    protected Component deceratedComponent;
    public DeceratorComponent(Component deceratedComponent) {
        this.deceratedComponent = deceratedComponent;
    }
}
/**
 * 具体的装饰类
 */
public class ConcreteDeceratorComponent extends DeceratorComponent {
    public ConcreteDeceratorComponent(Component deceratedComponent) {
        super(deceratedComponent);
    }
    @Override
    public void operate() {
        before();
        super.deceratedComponent.operate();
        after();
    }
    public void before(){
        System.out.println("在原逻辑之前增加了逻辑");
    }
    public void after(){
        System.out.println("在原逻辑之后增加了逻辑");
    }
}
public class DeceratorTest {
    public static void main(String[] args) {
        Component concreteCom = new ConcreteComponent();
        concreteCom.operate();
        System.out.println("============");
        DeceratorComponent decerator = new ConcreteDeceratorComponent(new ConcreteComponent());
        decerator.operate();
    }
}

运行结果:


具体实现的逻辑


============


在原逻辑之前增加了逻辑

具体实现的逻辑

在原逻辑之后增加了逻辑


设计模式的灵活应用. 假如当前具体类就只有一个. 我们就不需要定义抽象的Component了. 如何实现装饰模式呢?


那就让装饰器直接继承自原来的类就可以了:

c4256484b5ab4e60872b7a09d86e73e3_tplv-k3u1fbpfcp-zoom-1.png


二. 装饰器模式的特点:



  1. 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
  2. 装饰对象包含一个真实对象的引用(reference)
  3. 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
  4. 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。


三. 装饰器模式的使用场景:



以下情况可以考虑使用装饰器模式

  1. 需要扩展一个类的功能,或给一个类添加附加职责。
  2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
  4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。


四. 装饰器模式的优点:



  1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
  2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。


五. 装饰器模式的缺点



  1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
  2. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
  3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

六. 思考: 装饰器模式使用了哪些设计模式的原则?



  1. 最明显的体现就是开闭原则---对扩展开发, 对修改关闭. 当新的需求到达, 在不改变原来功能的基础上进行扩展.
  2. 依赖倒置原则---依赖于抽象, 而非具体. 方便扩展, 再来一种新房子, 不用修改房子装饰类.
  3. 单一职责原则: 一个类只负责一件事
相关文章
|
6月前
|
设计模式 存储 缓存
聊聊Java设计模式-装饰器模式
装饰器模式允许向一个现有的对象添加新的功能,同时不改变其结果。比如Java 中的IO框架中,`FileInputStream`(处理文件)、`ByteArrayInputStream`(处理字节数组)、`BufferedInputStream`(带缓存的处理类)等就是对`InputStream`进行的功能扩展,这就是装饰器模式的典型应用。
60 1
聊聊Java设计模式-装饰器模式
|
6月前
|
设计模式 Java
常用设计模式(工厂方法,抽象工厂,责任链,装饰器模式)
有关设计模式的其他常用模式请参考 单例模式的实现 常见的设计模式(模板与方法,观察者模式,策略模式)
60 2
|
6月前
|
设计模式
设计模式之装饰器模式
设计模式之装饰器模式
|
6月前
|
设计模式
设计模式-装饰器模式
设计模式-装饰器模式
|
1月前
|
设计模式 XML Java
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
29 0
|
6月前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
63 1
|
2月前
|
设计模式 Java
Java设计模式-装饰器模式(10)
Java设计模式-装饰器模式(10)
|
5月前
|
设计模式 Java
Java设计模式:深入装饰器模式的三种写法(六)
Java设计模式:深入装饰器模式的三种写法(六)
|
5月前
|
设计模式 架构师 安全
设计模式第五讲-装饰器模式和代理模式详解
远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
264 0
|
6月前
|
设计模式 传感器 数据处理
探索设计模式的魅力:为什么你应该了解装饰器模式-代码优化与重构的秘诀
装饰器模式是一种设计模式,它允许在运行时向对象添加额外的职责,而无需修改其代码。这种模式提供了一种动态扩展对象功能的方法,同时保持了对象的单一职责原则。本文介绍了装饰器模式的基本概念、原理、优势、适用场景、实现方法、最佳实践和注意事项。通过装饰器模式,可以将多个行为组合成一个更复杂的行为,而无需使用继承或大量的接口实现。装饰器模式适用于需要对一个对象进行一系列的增强处理的情况,而这些增强处理可以以一种松耦合的方式进行组合。通过使用装饰器模式,可以提高代码的可维护性、可扩展性和灵活性,使系统更加灵活和易于维护
121 1
探索设计模式的魅力:为什么你应该了解装饰器模式-代码优化与重构的秘诀