介绍和要点
Decorator Pattern, 又名Wrapper Pattern, 是一种比生成子类更”灵活”的动态为对象添加职责的设计模式.”灵活”加了引号只是为了提醒你通常使用上的灵活和写代码上的简便是两回事.
在 Decorator Pattern 中, 除了负责调用的Client外, 其余的主要角色通常为:
- Component: 需要包装的对象的基类
- ConcreteComponent: Component的子类, 被ConcreteDecorator所包装, 通常是多个
- Decorator: 包装者的基类, 继承Component且包含Component, 如果需要动态添加的行为不多, 那么这个类可以直接作为ConcreteDecorator.
- ConcreteDecorator: Decorator的子类, Client调用的包装者, 通常是直接接收ConcreteComponent作为其被包装者.
这些对象的定义看起来并不怎么直观, 但在画出UML类图之前, 需要注意的一点是: 在这四个主要角色里Decorator与Component的关系既有继承又有组合, 这样做的目的是为了灵活使用ConcreteComponent实现的父类方法, 进而对其进行包装. 而这样做的优点是:
- 比静态继承更灵活, 避免了子类数量爆炸
- Decorator对于ConcreteComponent是动态, 透明的, 在Client调用之前, 不会在不需要的行为组合上浪费时间
- 基于上一点, Decorator是可以递归叠加的.
当然装饰模式也并非完美:
Decorator和Component的关系相对复杂, 而这在Component本身就相对复杂时使用Decorator并不那么容易, 这时要考虑使用Strategy模式, 虽然Strategy并不是透明(指对任何一种Component)的.
典型结构
以Java的继承方式为例, 这里省略了具体的返回类型
在Client里, 你可以自由地为ConcreteCoponentA, B, C添加ConcreteDecoratorA, B提供的行为:
// client code segment
// c1是添加了X行为的A
Component c1 = new ConcreteDecoratorX(new ConcreteComponentA());
// c2是添加了Y行为的B
Component c1 = new ConcreteDecoratorY(new ConcreteComponentB());
// c3是添加了一个X, 两个Y行为的AA
Component c1 = new ConcreteDecoratorX(
new ConcreteDecoratorY(
new ConcreteDecoratorY(
new ConcreteComponentA()
)
)
);
一个例子
设计一个游戏角色类, 需要在某些情况下增加Buff或者Debuff, 影响其攻击(damage)和抗性(resistance)数值.
需要注意的是, Buff和Debuff都不是永久的, 如果完全使用Decorator完成的话, 我们至少需要设计一个回归初始数值的ConcreteDecorator 或者保存Buff前角色的数据结构.
当然, 在实际的gameloop里, 像游戏角色这样的通常是prototype克隆得来的, 装饰器模式当然可以和prototype共用, 又或者使用状态机来表示Buff. 总之, 这里只是展示DecoratorPattern的典型结构的使用, 它可能和实际的游戏开发使用方式截然不同. ```java abstract class Role{ private int damage; private int resistance;// Constructors…
// Getters and Setters… }
class Player implements Role{ // Constructors… }
class BuffDecorator implements Role{ private Role role; public BuffDecorator(Role r){this.role = r;}
private void IncreaseDamage(int x){
setDamage(r.getDamage() + x);
}
private void IncreaseResistance(int x){
setResistance(r.getResistance() + x);
}
}
class ResetStatBuff implements BuffDecorator{ public ResetStat(Role r){ super(r); // 默认值可以放在enum中,这里写成10只是为了简单 setDamage(10); setResistance(10); } }
class IncreaseDamageBuff implements BuffDecorator{ public ResetStat(Role r){ super(r); IncreaseDamage(5); } }
class IncreaseResistanceBuff implements BuffDecorator{ public ResetStat(Role r){ super(r); IncreaseResistance(5); } }
// In client… Role r = new Player(10,10); // make a buff added player r = new IncreaseDamageBuff(new IncreaseResistanceBuff(r)); // return to normal r = new ResetStatBuff(r); ```
实际例子
C++的Stream, Java的InputStream相关的类.