装饰模式
装饰模式能给一个对象动态添加一些额外的职责。就增加功能来说, Decorator 模式相比生成子类更为灵活。
装饰模式结构
在装饰模式中各个角色有:
- 抽象构件(Component)角色: 给出一个抽象接口,以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色:定义一个要接收附加责任的类。
- 装饰(Decorator)角色:持有一个构件(断肠和)对象的实例,并定义一个与抽象构件接口一致的接口。
- 具体装饰(Decorator)角色:负责给构件对象 “贴上”附加的责任。
例子:
- 装饰模式动态地为对象附加额外的责任。添加到松树或冷杉树上的装饰品就是装饰模式的例子。灯光、花环、手杖糖果、玻璃装饰品等,都可以添加到树上,让它看起来更喜庆。这些装饰物不会改变圣诞树本身,不管使用什么特殊的装饰物,圣诞树都可以被认作圣诞树。作为附加功能的一个例子,灯的添加允许人们“点亮”圣诞树。
- 另一个例子:突击枪本身就是致命武器。但你可以应用某些“装饰”使它更精确、更安静、更具破坏性。
我们再从代码层面看一个例子:
使用装饰模式前
package DecoratorPattern.Before;
/**
* 使用装饰模式前
*/
class A {
public void doIt(){
System.out.print('A');
}
}
class AwithX extends A {
@Override
public void doIt() {
super.doIt();
doX();
}
private void doX(){
System.out.print('X');
}
}
class AwithY extends A {
@Override
public void doIt() {
super.doIt();
doY();
}
public void doY(){
System.out.print('Y');
}
}
class AwithZ extends A {
@Override
public void doIt() {
super.doIt();
doZ();
}
public void doZ() {
System.out.print('Z');
}
}
class AwithXY extends AwithX {
private AwithY obj = new AwithY();
@Override
public void doIt() {
super.doIt();
obj.doY();
}
}
class AwithXYZ extends AwithX {
private AwithY obj1 = new AwithY();
private AwithZ obj2 = new AwithZ();
@Override
public void doIt() {
super.doIt();
obj1.doY();
obj2.doZ();
}
}
public class DecoratorDemo{
public static void main(String[] args) {
A[] array = {new AwithX(), new AwithXY(), new AwithXYZ()};
for (A a: array){
a.doIt();
System.out.print(' ');
}
}
}
使用装饰模式后
package DecoratorPattern.After;
/**
* 使用装饰模式后
*/
interface I {
void doIt();
}
class A implements I {
@Override
public void doIt() {
System.out.print('A');
}
}
abstract class D implements I {
private I core;
public D(I inner) {
core = inner;
}
@Override
public void doIt() {
core.doIt();
}
}
class X extends D {
public X(I inner) {
super(inner);
}
@Override
public void doIt() {
super.doIt();
doX();
}
private void doX() {
System.out.print('X');
}
}
class Y extends D {
public Y(I inner) {
super(inner);
}
@Override
public void doIt() {
super.doIt();
doY();
}
private void doY() {
System.out.print('Y');
}
}
class Z extends D {
public Z(I inner) {
super(inner);
}
@Override
public void doIt() {
super.doIt();
doZ();
}
private void doZ() {
System.out.print('Z');
}
}
public class DecoratorDemo {
public static void main(String[] args) {
I[] array = {new X(new A()), new Y(new X(new A())), new Z(new Y(new X(new A())))};
for (I anArray: array){
anArray.doIt();
System.out.print(' ');
}
}
}
优点:
- 装饰模式比静态继续更灵活。与对象的静态继承相比,装饰模式提供了更加灵活地向对象添加职责的方式。可以用添加和分享的方法,用装饰在运行时和删除职责。相比之下,继承机制要求为每个添加的职责创建一个新的子类。这会产生许多新的类,并且会增加系统复杂度。此外,为下个特定的 Component 类提供多个不同的 Decorator 类,这就使得你可以对一些职责进行混合和匹配。
- 避免在层次结构高层的类有太多的特征。装饰模式提供了一种”即用即付“的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并且用 Decorator 类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能。这样,应用程序不必为不需要的特征付出代价。
缺点:
- Decorator 与它的 Component 不一样。Decorator 是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
- 有许多小对象。采用装饰模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在它们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对天那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。
模式的简化
- 没有抽象的接口 Component 也是可以的,但 ConcreteComponent 就要扮演双重角色。
- 没有抽象的 Decorator 也是可以的,只是 ConcreteDecorator 需要扮演双重的角色。
参考: