本文主要介绍:装饰模式概念和使用,以及对应的透明和半透明形式概念。
模式背景
假设我们有一堆的类,这些类我们需要为他扩充一些功能。比如在执行该类的方法前或者后增加一些逻辑,该怎么做?相信你一定就已经能想到了装饰模式的用法!但是我们还是要说说最笨的方法:
最糟糕的设计就是,我们创建一个新的类去继承原始类,然后overwrite原来的方法。这很糟糕的点在于
- 会产生大量的类。如果我们要对所有的类扩展一下,那么每个类都继承一个新类?NO!
- 继承这个东西,不同语言实现机制不同,可能实现功能会受限制。有些语言支持多重继承,有些就不支持。
我们想复用的时候,应当多用关联,少用继承。装饰模式就是用关联的方式为类扩展新的功能的。
定义&概念
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。装饰模式是一种对象结构型模式。
原理
装饰模式的思想很简单:我们可以将需要装饰的东西提取出来,创建一个专门用于装饰的类。然后这个类里面关联一个需要被装饰的对象。在使用的时候,我们先创建好需要被装饰的对象,然后用这个对象再通过装饰类去构造装饰好的对象。
可以理解为:一个人需要穿衣服(这个人就是要被装饰的类),现在他可能有好几套衣服(装饰类)。我们需要这个人穿什么衣服,就把人塞进这个衣服里面就行了。
组成要素
- Component(抽象构建类)
- 为了方便扩展的一个顶层抽象,创建这个类或者接口的目的是为了让客户端可以面向抽象编程,还有一个作用就是为了让原始类可以方便的扩展。
- 这个抽象里面提供着被装饰者的一些操作,这样客户端就可以完全面向Component编程了。
- ConcreteComponent(具体构建)
- 抽象类的实现,就是那些需要被装饰的对象的类。原始类。
- Decorator(抽象装饰类)
- 创建这个要素的原因:我们存在着装饰类也会扩展的可能,这个属于所有装饰器的抽象。
- 该抽象内部维持一个抽象构建类的一个引用。申明需要装饰增加的职责,但是具体的实现交给装饰实现类。该抽象装饰类的设计是装饰模式的核心。
- ConcreteDecorate(具体装饰类)
- 负责装饰职责的实现。即为原始类的方法添加具体的扩展逻辑的。
透明装饰模式
我们上面所说的装饰模式就是透明装饰模式,也是一般所指的装饰模式。
半透明装饰模式
**因为透明模式存在一个问题:客户端想要使用装饰者的方法。**我们前面是面向Component抽象编程的了,没法调用到装饰类的方法。这时候我们就不能完全面向抽象编程了,我们使用的时候就需要使用装饰类来引用具体的对象。这就是半透明装饰模式。但是我们一般都是使用的透明的装饰模式。
UML
实现
抽象构建类
public interface Component {
void display();
}
抽象装饰类
public abstract class ComponentDecorator implements Component {
/**重点 内部组合一个需要被装饰对象引用*/
private Component component;
public ComponentDecorator(Component component) {
this.component = component;
}
/**抽象类中没有对齐进行装饰,具体装饰交给装饰器实现类来做*/
@Override
public void display() {
component.display();
}
}
实体构建类
public class ComponentA implements Component{
@Override
public void display() {
System.out.println("A");
}
}
装饰器
public class ComponentDecorator extends ComponentDecorator {
public ComponentDecorator(Component component) {
super(component);
}
@Override
public void display() {
System.out.println("装饰--开始");
super.display();
System.out.println("装饰--结束");
}
}
使用
/**定义多个装饰器,一个对象可以被返回装饰多次*/
Component c1 = new ComponentA();
Component c2 = new ComponentDecoratorA(c1);
Component c3 = new ComponentDecoratorB(c2);
c3.display();
注意事项
- 构建实现类尽量保证轻量级,复杂的逻辑可以通过装饰去做(es的query builder 不就是装饰一个个聚合函数吗)
- 尽量使用透明的装饰模式,即装饰接口和构建接口一致。
- 如果系统确定对象只有一个具体的构建类,我们就不需要抽象构建类了,装饰抽象可以作为该具体构建的子类。
使用场景
这个使用场景就太多了,只要你发现一个类不能满足我的需求,希望在这个类上扩展一些功能的就会使用到。
总结
装饰模式就是:
- 现将装饰的部分提取出来
- 做2个抽象:一个是被装饰的抽象,一个是装饰类的抽象
- 需要装饰的时候,将被装饰对象,使用装饰类包裹下。
他的实现手法和理念其实和前面很多模式有相似的地方,也很容易搞混!下面说说他们的区别:
和代理模式区别
这个之前写代理模式也提到过,这里简单描述下:和代理模式乍一看是一样的嘛!仔细一看还是一样的嘛!他们的不同之处更多的体现在思想上:
- 装饰模式强调增强自身,自己还是自己,只是穿上了衣服的自己。外面打交道人家也只是认为你是穿了衣服的你罢了。
- 代理模式则强调控制,你已经不完全是你了,你找了个代理人帮你做事情,外面人家不认你,只认你的代理人,代理人怎么搞你都可以。还有就是代理模式一般都是做你本身不该做,或者做不了的事情的。
还有一点区别就是装饰类的扩展性!可以再UML上看到装饰类是有一层抽象的,而代理没有,因为代理
和适配器模式的区别
这个前面也提到过。适配器的UML和装饰模式还是有很大区别的。而且适配嘛,和装饰字面意思上也有差别。 客户端那边使用装饰和使用原对象是一样用的,但是适配不一样,适配是因为客户端用不了原对象的,所以用的适配器类,这个类本身和原来的类没有任何关系。他是为了满足客户端能使用来设计的类。服务对象是客户端!而装饰器服务对象是要被装饰的类!主体都不同。
我自己个人感觉:这些模式的思想其实都是利用代理的思想!他们真的很像!我们实际用的时候可能就没必要这么抠字眼了,他们的思想其实都是基于代理的思想,所以代理模式代表性的东西,然后因为性质的不同分了这些装饰还是适配的。他们是有共同思想的,这种思想把握了就能优化系统的设计不是吗?(再次强调下:这只是我个人的感觉)
附
如有代码和文章问题,还请指正!感谢!