设计模式之装饰器模式

装饰器模式

装饰器模式

装饰器模式(Decorator Pattern),也称为包装模式(Wrapper Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

装饰器模式的核心是功能扩展。使用装饰器模式可以透明且动态地扩展类的功能。

装饰器模式主要用于透明且动态地扩展类的功能。其实现原理为:让装饰器实现被包装类(Concrete Component)相同的接口(Component 使得装饰器与被扩展类类型一致),并在构造函数中传入该接口(Component)对象,然后就可以在接口需要实现的方法中在被包装类对象的现有功能上添加新功能了。而且由于装饰器与被包装类属于同一类型(均为Component),且构造函数的参数为其实现接口类(Component),因此装饰器模式具备嵌套扩展功能,这样我们就能使用装饰器模式一层一层的对最底层被包装类进行功能扩展了。

首先看下装饰器模式的通用UML类图:

抽象组件(Component):可以是一个接口或者抽象类,其充当被装饰类的原始对象,规定了被装饰对象的行为;

具体组件(ConcreteComponent):实现/继承Component的一个具体对象,也即被装饰对象;

抽象装饰器(Decorator):通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component抽象组件;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个Component抽象组件,这是强制的通用行为(当然,如果系统中装饰逻辑单一,并不需要实现许多装饰器,那么我们可以直接省略该类,而直接实现一个具体装饰器(ConcreteDecorator)即可);

具体装饰器(ConcreteDecorator):Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能;

通用写法

抽象组件(Component)

1
2
3
4
public interface Component
{
void operation();
}

具体组件(ConcreteComponent)

1
2
3
4
5
6
7
8
public class ConcreteComponent implements Component
{
@Override
public void operation()
{
System.out.println("ConcreteComponent operation");
}
}

抽象装饰器(Decorator)

1
2
3
4
5
6
7
8
9
public abstract class Decorator implements Component
{
protected Component component;

public Decorator(Component component)
{
this.component = component;
}
}

具体装饰器(ConcreteDecorator)ConcreteDecoratorA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ConcreteDecoratorA extends Decorator
{
public ConcreteDecoratorA(Component component)
{
super(component);
}

@Override
public void operation()
{
operationFirst();
component.operation();
operationLast();
}

private void operationFirst()
{

System.out.println("DecoratorA first");
}

private void operationLast()
{
System.out.println("DecoratorA last");
}
}

具体装饰器(ConcreteDecorator)ConcreteDecoratorB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ConcreteDecoratorB extends Decorator
{
public ConcreteDecoratorB(Component component)
{
super(component);
}

@Override
public void operation()
{
operationFirst();
component.operation();
operationLast();
}

private void operationFirst()
{

System.out.println("DecoratorB first");
}

private void operationLast()
{
System.out.println("DecoratorB last");
}
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
public class Main
{
public static void main(String[] args)
{
ConcreteComponent component = new ConcreteComponent();
ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(component);
ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(component);

concreteDecoratorA.operation();
concreteDecoratorB.operation();
}
}

运行结果

应用场景

装饰器在代码程序中适用于以下场景:

1、用于扩展一个类的功能或给一个类添加附加职责。

2、动态的给一个对象添加功能,这些功能可以再动态的撤销。

3、需要为一批的兄弟类进行改装或加装功能。

装饰器模式在源码中的应用

装饰器模式在源码中也应用得非常多,在JDK中体现最明显的类就是I0相关的类,如BufferedReader、 InputStream、 OutputStream。

在Spring中的TransactionAwareCacheDecorator类也是装饰器模式,这个类主要是用来处理事务缓存的,来看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TransactionAwareCacheDecorator implements Cache {

private final Cache targetCache;


/**
* Create a new TransactionAwareCache for the given target Cache.
* @param targetCache the target Cache to decorate
*/
public TransactionAwareCacheDecorator(Cache targetCache) {
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}
...
}

TransactionAwareCacheDecorator就是对Cache的一个包装。

Spring MVC中的装饰器模式,HttpHeadResponseDecorator类也是装饰器模式。

装饰器模式和代理模式对比

从代理模式的UML类图和通用代码实现上看,代理模式与装饰器模式几乎一模一样。代理模式的Subject对应装饰器模式的Component,代理模式的RealSubject对应装饰器模式的ConcreteComponent,代理模式的Proxy对应装饰器模式的Decorator。确实,从代码实现上看,代理模式的确与装饰器模式是一样的(其实装饰器模式就是代理模式的一个特殊应用),但是这两种设计模式所面向的功能扩展面是不一样的:

装饰器模式强调自身功能的扩展。Decorator所做的就是增强ConcreteComponent的功能(也有人可能减弱功能),主体对象为ConcreteComponent,着重类功能的变化;

代理模式强调对代理过程的控制。Proxy完全掌握对RealSubject的访问控制,因此,Proxy可以决定对RealSubject进行功能扩展,功能缩减甚至功能散失(不调用RealSubject方法),主体对象为Proxy

举例来说,假设现在小明想租房,那么势必会有一些事务发生:房源搜索,联系房东谈价格…

假设我们按照代理模式进行思考,那么小明只需找到一个房产中介,让他去干房源搜索,联系房东谈价格这些事情,小明只需等待通知然后付点中介费就行了;

而如果采用装饰器模式进行思考,因为装饰器模式强调的是自身功能扩展,也就是说,如果要找房子,小明自身就要增加房源搜索能力扩展,联系房东谈价格能力扩展,通过相应的装饰器,提升自身能力,一个人做满所有的事情。

装饰器模式的优缺点

优点:

1、装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。

2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。

3、装饰器完全遵守开闭原则。

缺点:

1、会出现更多的代码,更多的类,增加程序复杂性。

2、动态装饰时,多层装饰时会更复杂。

打赏

请我喝杯咖啡吧~

支付宝
微信