一、模式动机
有时我们希望给某个对象而不是整个类添加一些功能。例如:一个图形用户界面工具箱允许我们对任意一个用户界面组件添加一些新的特性,如增加一个边框;或者增加一些行为,如窗口的滚动
一种较为灵活的方式是将组件嵌入另一个对象中,由这个对象来添加边框。我们称这个嵌入的对象为装饰器(Decorator)。这个装饰与它所装饰的组件接口 一致,因此它对使用该组件的客户透明。它将客户请求转发给该组件,并且可 能在转发前后执行一些额外的动作。这种透明性使得我们可以递归嵌套多个装饰,从而可以添加任意多的功能。
二、模式定义
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责 (Responsibility),就增加对象功能来说,装饰模式比生成子类实现 更为灵活。其别名为包装器(Wrapper)。装饰模式是一种对象结构型模 式。
三、模式结构
四、参与者
- Component:组件
- ConcreteComponent:具体组件
- Decorator:抽象装饰类
- ConcreteDecorator:具体装饰类
五、示例代码
package design.pattern;
abstract class Appearence {
public abstract void show();
}
class Person extends Appearence {
private String name;
public Person(String name) {
this.name = name;
}
public void show() {
System.out.println("装扮的" + name);
}
}
abstract class Finery extends Appearence {
protected Appearence component;
public void decorate(Appearence component) {
this.component = component;
}
public void show() {
if (component != null)
component.show();
}
}
class TShirts extends Finery {
public void show() {
super.show();
System.out.println("T恤 ");
}
}
class Jeans extends Finery {
public void show() {
super.show();
System.out.println("牛仔裤 ");
}
}
class Sneakers extends Finery {
public void show() {
super.show();
System.out.println("球鞋 ");
}
}
public class Decorator {
public static void main(String[] args) {
Person xm = new Person("小明");
Sneakers qx = new Sneakers();
Jeans nzk = new Jeans();
TShirts tx = new TShirts();
tx.decorate(xm);
nzk.decorate(tx);
qx.decorate(nzk);
qx.show();
}
}
模式结构
优点
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
- 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错。
缺点
采用装饰模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。模式使用
- 在不影响其它对象的情况下,以动态、透明的方式给单个对象添加职责。需要动态地给一个对象增加功能,这些功能可以再动态地被撤销。
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义不能继承(final类),或类不能用于生成子类。
需要注意的问题
- 一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。
- 尽量保持具体构件类Component作为一个“轻”类,也就是说不要把太多的逻辑和状态放在具体构件类中,可以通过装饰类对其进行扩展。