设计模式之装饰者模式

来源:互联网 发布:女性健康知讲座 编辑:程序博客网 时间:2024/06/05 00:47

一、模式动机

  使用继承可以给现有类添加行为,但是这种方法是静态的,用户不能控制增加行为的方式和时机。因此可以使用关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)
  装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的模式动机。

二、模式定义

  装饰者模式(Decorator Pattern):动态地将责任附加到对象上。如要扩展功能,装饰者提供了比继承更有弹性的替代方案。包括抽象组件、具体组件、抽象装饰者和具体装饰者。
  
这里写图片描述

三、模式示例

  星巴兹咖啡连锁店由于扩张速度太快了,准备更新订单系统,以满足他们的饮料供应要求。他们的类设计如下:
  
这里写图片描述

购买咖啡,可以要求在其中加入配料,例如:牛奶,摩卡等。星巴兹会根据所加入的配料收取不同的费用。价格表如下:

这里写图片描述

如果用继承的话,设计出很多类。采用装饰者模式来解决问题,UML类图如下:

这里写图片描述

C++代码实现

#include <string>#include <iostream>#include "windows.h"using namespace std;class Beverage{public:    virtual double cost() = 0;    virtual string getDescription() const { return description_; }    Beverage();    ~Beverage();protected:    string description_;};Beverage::Beverage()    :description_("Unknown Beverage"){}Beverage::~Beverage(){}//综合class HouseBlend : public Beverage{public:    double cost() { return 0.89; }    string getDescription() const { return description_; }    HouseBlend();    ~HouseBlend();};HouseBlend::HouseBlend(){    description_ = "HouseBlend";}HouseBlend::~HouseBlend(){}//浓缩咖啡class Espresso : public Beverage{public:    double cost() { return 1.99; }    string getDescription() const { return description_; }    Espresso();    ~Espresso();};Espresso::Espresso(){    description_ = "Espresso";}Espresso::~Espresso(){}//深焙咖啡class DarkRoast : public Beverage{public:    double cost() { return 0.99; }    string getDescription() const { return description_; }    DarkRoast();    ~DarkRoast();private:};DarkRoast::DarkRoast(){    description_ = "DarkRoast";}DarkRoast::~DarkRoast(){}//低咖啡因class Decaf : public Beverage{public:    double cost() { return 1.05; }    string getDescription() const { return description_; }    Decaf();    ~Decaf();};Decaf::Decaf(){    description_ = "Decaf";}Decaf::~Decaf(){}//抽象装饰者:调料Condimentclass Condiment : public Beverage{public:    Condiment(Beverage*  beverage);    ~Condiment();protected:    Beverage* beverage_;};Condiment::Condiment(Beverage*  beverage)    :beverage_(beverage){}Condiment::~Condiment(){}//调料:摩卡class Mocha : public Condiment{public:    double cost(){ return 0.2 + beverage_->cost(); }    string getDescription() const { return beverage_->getDescription() + " , Mocha"; }    Mocha(Beverage*  beverage);    ~Mocha();};Mocha::Mocha(Beverage*  beverage)    :Condiment(beverage){}Mocha::~Mocha(){}//调料:牛奶class Milk : public Condiment{public:    double cost(){ return 0.1 + beverage_->cost(); }    string getDescription() const { return beverage_->getDescription() + " , Milk"; }    Milk(Beverage*  beverage);    ~Milk();};Milk::Milk(Beverage*  beverage)    :Condiment(beverage){}Milk::~Milk(){}//调料:豆浆class Soy : public Condiment{public:    double cost(){ return 0.15 + beverage_->cost(); }    string getDescription() const { return beverage_->getDescription() + " , Soy"; }    Soy(Beverage*  beverage);    ~Soy();};Soy::Soy(Beverage*  beverage)    :Condiment(beverage){}Soy::~Soy(){}//调料:奶泡class Whip : public Condiment{public:    double cost(){ return 0.1 + beverage_->cost(); }    string getDescription() const { return beverage_->getDescription() + " , Whip"; }    Whip(Beverage*  beverage);    ~Whip();};Whip::Whip(Beverage*  beverage)    :Condiment(beverage){}Whip::~Whip(){}int _tmain(int argc, _TCHAR* argv[]){    Beverage*  beverage = new Espresso;    cout << beverage->getDescription() << " $" << beverage->cost() << endl;    beverage = new  Mocha(beverage);    cout << beverage->getDescription() << " $" << beverage->cost() << endl;    beverage = new  Milk(beverage);    cout << beverage->getDescription() << " $" << beverage->cost() << endl;    beverage = new  Soy(beverage);    cout << beverage->getDescription() << " $" << beverage->cost() << endl;    beverage = new  Whip(beverage);    cout << beverage->getDescription() << " $" << beverage->cost() << endl;    system("pause");    return 0;}

运行结果

这里写图片描述

四、分析总结

  装饰模式用于动态地给一个对象增加一些额外的职责,就增加对象功 能来说,装饰模式比生成子类实现更为灵活。它是一种对象结构型模 式。

优点:

  1. 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
  2. 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
  3. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
  4. 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

缺点:

  1. 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
  2. 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。