装饰者(Decorator)模式

参考:廖雪峰的博客

介绍

类型:结构型模式。

定义:在不改变原有对象的基础上,将功能附加到对象上。

优势:提供了比集成更有弹性的替代方案(扩展原有对象功能)。

整体UML类图

装饰者(Decorator)模式

代码中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//先定义一个“具体构件”实例
ConcreteComponent concreteComponent = new ConcreteComponent();

//定义具体装饰者类,并传入“具体构件实例”
DecoratorAImpl A = new DecoratorAImpl(concreteComponent);
DecoratorBImpl B = new DecoratorBImpl(concreteComponent);

//调用具体的“装饰者类”的方法,可以实现额外功能。
// 具体的功能实现些在DecoratorAImpl/DecoratorBImpl中
A.operation();//A有附加功能a
B.operation();//B有附加功能b

/*******其实以上单独的功能直接用继承也能实现,重点是下面的组合功能*******/

//如果你想要同时添加附加功能a、b,这样即可
DecoratorBImpl decoratorBA = new DecoratorBImpl(A);
decoratorBA.operation();

这样写的优势

小伙伴们可能会疑惑,我所有需要用到的实体类直接继承Component接口不就好了,何必如此麻烦?

答:组合使用,更灵活。

现有业务场景如下:

  1. Component中定义的operation()方法为业务核心功能,所有继承它的类都必须实现。
  1. 现有一些附加功能A、附加功能B 和 附加功能C。

好了,现在你要开始用这个Component了。

你完全可以定义三个类继承Component各自实现其附加功能ABC,需要用的时候直接使用。至此一切都没问题,直到领导给你提了第三个需求:

  1. 需要附加功能AB。

这个时候你应该不太愿意会又写一个类,来继承Component,以实现附加功能AB。因为你知道,领导的下一个需求将会是BC(或AC)。

这里只有三个附加功能,如果附加功能多了的话,排列组合起来将会是个爆炸数字。想到这里,继承的方式已经不可取了。

现在你再回过头来看上面☝的使用代码。

image-20200927233452673.png

文章开头写的 提供了比集成更有弹性的替代方案(扩展原有对象功能) 正是这个意思。

实际中的运用

  • Java源码中的InputStream

InputStream相关类

其中,InputStream既是Component,也是ConcreteComponent。通过传入不同的InputStream对象,可以实现不同的功能。

要想实现FilterInputStream,可以:

1
2
// 创建原始的数据源:
InputStream fis = new FileInputStream("test.gz");

想加上缓冲功能,可以:

1
2
// 增加缓冲功能:
InputStream bis = new BufferedInputStream(fis);

再加个CCache(缓存?)功能,可以:

1
2
//继续增加功能
InputStream cis = new CCacheInputStream(bis);
  • Android源码中的Context

Context相关类

如图所示,ApplicationActivityService 这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的,Android将很多调用底层数据的相关方法封装到了ContextImpl中。(ContextWrapper中几乎所有的方法实现都是调用ContextImpl的相应方法来实现的。)

理解了这一层,对理解Context结构有很大帮助。

相关设计模式

  • 装饰者(Decorator)模式 和 代理模式

    装饰者模式关注点在于动态的添加方法;代理模式关注于控制对对象的访问。

    代理模式中,我们通常在代理类中使用类的实例;而在装饰者模式中,我们通常会把原始对象当做一个参数传给装饰者的构造器。