(learn&think)

不浮躁,不自傲,学习,思考,总结

浅谈设计模式三: 装饰器模式(Decorator)

| Comments

1 实例

星巴克以咖啡文明,现在为它设计它的咖啡订餐系统。

  1. 先定义一类饮料的基类 Beverage ,为不同咖啡做好一个接口。
  2. 每种不同的咖啡继承这个基类,覆盖各自的特性,比如描述,价格等。

每种咖啡都有很多中附加,比如拿铁咖啡就有,加糖拿铁,加奶拿铁,加奶加糖拿铁等等。所以按照上面的设计来设计咖啡订餐系统,那么整个系统类结构如下:

每种咖啡不同的附加组合导致太多类。

2 装饰器模式(Decorator)

2.1 目的

为一个对象动态的附加额外的职责。装饰器(Decorators)提供除了用子类的另外一种灵活的扩充功能方法。

2.2 实现

2.2.1 定义组件抽象接口和具体组件

1.定义组件抽象接口:咖啡和装饰器的基类

class Beverage {
 public:
  Beverage();
  virtual ~Beverage();
  virtual string description();
  virtual double cost() = 0;
 protected:
  string description_;
};

2.定义不同的组件:不同类型的咖啡

class DarkRoast : public Beverage {
 public:
  DarkRoast();
  virtual ~DarkRoast();
  virtual double cost();
};

2.2.2 定义抽象装饰器和具体装饰器

装饰器和被装饰的对象必须拥有相同的基类。

1.定义装饰器抽象接口(继承于组件抽象接口):装饰器的基类

class CondimentDecorator : public Beverage {
 public:
  virtual ~CondimentDecorator() {}
  virtual string description() = 0;
};

2.定义不同的装饰器:不同咖啡的配料维持组件对象的一个引用,并定义与组件接口一致的接口。

class Milk : public CondimentDecorator {
 public:
  explicit Milk(Beverage *beverage);
  virtual ~Milk();
  virtual string description();
  virtual double cost();
 private:
  Beverage *beverage_;
};

2.2.3 整合到一起

Beverage *beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
cout << beverage2->description() << " $" << beverage2->cost() << endl;

2.2.4 总的框架

3 总结

3.1 装饰器模式(Decorator)结构

3.2 组成

  • 抽象组件(Component)定义一个对象接口,使得装饰对象能动态加入到组件中。
  • 具体组件(ConcreteComponent)定义一个能附加职责的类。
  • 抽象装饰器(Decorator)维持组件对象的一个引用,并定义与组件接口一致的接口。
  • 具体装饰器(ConcreteDecorator)添加职责给组件。

3.3 应用场景

  • 为独立的对象动态并透明的添加职责,也就是,不影响其他对象。
  • 处理能收回的职责。
  • 当不能通过子类来扩充时。有时候一大堆独立的扩充是可能的,但将激增子类来支持每种扩充间的组合。或者一个类的定义可能被隐藏或不能用于生成子类。

3.4 缺点

  1. 装饰器和它的组件不是一样的。一个装饰器扮演着透明的附加。但是从对象标识的角度来看,一个被装饰的组件和组件本身并不一致。所以当你使用装饰器时,不应该依赖对象标识。
  2. 许多小的对象。一个使用装饰器的设计经常导致系统被许多类似的小的对象所组成。这些小的对象仅仅不同在它们内在的关联,不是它们的类或参数的值。虽然这些系统很容易被熟知他们的人定置,但它们很难被了解和调试。

Comments