C++设计模式笔记(06) - Decorator 装饰模式



  • 参考课程:《C++设计模式》-李建忠
    李建忠-C++设计模式

0."单一职责"模式

在这里插入图片描述
▷在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。

▷典型模式
• Decorator
• Bridge

1.动机(Motivation)

在这里插入图片描述
▷在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。

▷如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?

2.模式定义:

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。
                  ——《设计模式:可复用面向对象软件的基础》

3.结构(Structure)

在这里插入图片描述

3.要点总结

在这里插入图片描述

  • 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。
  • 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
  • 组合和委托可用于在运行时动态地加上新的行为。
  • 除了继承,装饰者模式也可以让我们扩展行为。
  • 装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
  • 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。
  • 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
  • 你可以用无数个装饰者包装一个组件。
  • 装饰者般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
  • 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。

4.《Head First 设计模式》实例的C++实现

在这里插入图片描述

  • 为星巴兹咖啡连锁店设计饮料菜单,咖啡可加的调料有豆浆、牛奶、摩卡等。可在咖啡的基础上加入不同的调料,星巴兹会根据所加的调料收取不同的费用,要注意到,以后可能有新的调料被加入进来供顾客选择。并且本店现有DarkRoast(深焙)、HouseBlend(综合)、Decaf(低咖啡因)及Espresso(浓咖啡)四种类型的咖啡,而且以后可能会添加新的咖啡种类。

  • 星巴滋饮料店的系统需要更新一下,他们原来的系统是这样的:  
    在这里插入图片描述

  • 使用装饰者模式,四种咖啡为具体组件,调料为具体装饰者。
    在这里插入图片描述

  • 代码:
    1.抽象组件角色:Beverage(饮料)

//Beverage.h
//抽象组件角色:Beverage(饮料)
#ifndef DECORATOR_BEVERAGE_H
#define DECORATOR_BEVERAGE_H

#pragma once

#include <string>

using namespace std;

//Beverage是一个抽象类,有两个方法getDescription()及cost()
//装饰者和被装饰对象的共同基类
class Beverage
{
protected:
    string description;
public:
    Beverage(void);

    virtual string getDescription();    //getDescription已经在此实现了
    virtual double cost() = 0;   //cost()必须在子类中实现

    ~Beverage(void);
};

#endif //DECORATOR_BEVERAGE_H
//Beverage.cpp

#include "Beverage.h"

Beverage::Beverage(void): description("Unknown Beverage")
{
}

string Beverage::getDescription()
{
    return description;
}

Beverage::~Beverage(void)
{
}

2.具体组件角色:四种类型的咖啡
(1)Espresso(浓缩咖啡)

//Espresso.h
//具体组件角色:四种类型的咖啡-Espresso(浓缩咖啡)
#ifndef DECORATOR_ESPRESSO_H
#define DECORATOR_ESPRESSO_H

#pragma once
#include "Beverage.h"

//具体被装饰者
//首先让Espresso扩展自Beverage类,因为Espresso是一种饮料
class Espresso : public Beverage
{
public:
    Espresso(void);

    double cost();

    ~Espresso(void);
};

#endif //DECORATOR_ESPRESSO_H
//Espresso.cpp
#include "Espresso.h"

Espresso::Espresso(void)
{
    //为了要设置饮料的描述,我们写了一个构造器。记住,description()实例变量集成自Beverage。
    description = "Espresso";
}

double Espresso::cost()
{
    //最后,需要计算Espresso的价钱,
    // 现在不需要管调料的价钱,直接把Espresso的价格$1.99返回即可
    return 1.99;
}

Espresso::~Espresso(void)
{
}

(2)HouseBlend(家常咖啡)

//HouseBlend.h
具体组件角色:四种类型的咖啡-HouseBlend(家常咖啡)
#ifndef DECORATOR_HOUSEBLEND_H
#define DECORATOR_HOUSEBLEND_H

#pragma once
#include "Beverage.h"

//具体被装饰者
class HouseBlend : public Beverage
{
public:
    HouseBlend(void);

    double cost();

    ~HouseBlend(void);
};

#endif //DECORATOR_HOUSEBLEND_H
//HouseBlend.cpp
#include "HouseBlend.h"

HouseBlend::HouseBlend(void)
{
    description = "House Blend Coffee.";
}

double HouseBlend::cost()
{
    //这是另一种饮料,做法和Espresso一样
    //直是把Espresso名称改为"House Blend Coffee",
    //并返回正确的价钱$0.89
    return .89;
}

HouseBlend::~HouseBlend(void)
{
}

3.装饰者角色:CondimentDecorator(调料)

//装饰者角色: CondimentDecorator(调料)抽象类
CondimentDecorator.h

#ifndef DECORATOR_CONDIMENTDECORATOR_H
#define DECORATOR_CONDIMENTDECORATOR_H

#pragma once
#include "Beverage.h"

//装饰者,调料
//必须让CondimentDecorator能够取代Beverage,所以讲CondimentDecorator扩展自Beverage
class CondimentDecorator : public Beverage
{
public:
    CondimentDecorator(void);

    virtual string getDescription() = 0;    //所有的调料装饰者都必须重新实现getDescription()方法。

    ~CondimentDecorator(void);
};

#endif //DECORATOR_CONDIMENTDECORATOR_H
//CondimentDecorator.cpp
#include "CondimentDecorator.h"

CondimentDecorator::CondimentDecorator(void)
{
}

CondimentDecorator::~CondimentDecorator(void)
{
}

4.具体装饰者:各种调料

//Mocha.h
//具体装饰者:各种调料-Mocha(摩卡)
#ifndef DECORATOR_MOCHA_H
#define DECORATOR_MOCHA_H

#pragma once
#include "CondimentDecorator.h"

//Mocha是一个装饰者,所以让它扩展自CondimentDecorator。
//别忘了,CondimentDecorator扩展自Beverage
class Mocha : public CondimentDecorator
{
private:
    //要让Mocha能够引用一个Beverage,做法如下:
    //(1)用一个实例变量记录饮料(被装饰者)。
    //(2)想办法让饮料(被装饰者)被记录到实例变量中。这里的做法是:
    //  把饮料(被装饰者)当做构造器的参数,再由构造器将此饮料(被装饰者)记录在实例变量中。
    Beverage* beverage;   //用一个成员变量记录被装饰者
public:
    Mocha(Beverage* b);   //把被装饰者当做构造函数的参数,再由构造函数记录到成员变量中

    string getDescription();
    double cost();

    ~Mocha(void);
};

#endif //DECORATOR_MOCHA_H
//Mocha.cpp
#include "Mocha.h"

Mocha::Mocha(Beverage *b)
{
    beverage = b;
}

string Mocha::getDescription()
{
    //我们希望叙述不只是描述饮料(例如"DarkRoast"),
    //而是完整地连调料都描述出来(列入"DarkRoast,Mocha")。
    //所以首先利用委托的做法,得到一个叙述,
    //然后在其后加上附加的叙述(例如"Mocha")。
    return beverage->getDescription() + ", Mocha";
}

double Mocha::cost()
{
    //要计算带Mocha饮料的钱。
    //首先把调用委托给被装饰对象,以计算价钱,
    //然后加上Mocha的价钱,得到最后结果。
    return .20 + beverage->cost();
}

Mocha::~Mocha(void)
{
}

5.测试:

//装饰者模式:动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案

//OO原则:对扩展开放,对修改关闭

#include <iostream>
#include "Beverage.h"
#include "CondimentDecorator.h"
#include "Espresso.h"
#include "HouseBlend.h"
#include "Mocha.h"

using namespace std;

int main()
{
    Beverage* beverage = new Espresso;   //订一杯无调料的Espresso
    //打印出它的描述与价钱
    cout << beverage->getDescription() << " $" << beverage->cost() << endl;

    //本例没有建立DarkRoast类

    Beverage* beverage3 = new HouseBlend;
    beverage3 = new Mocha(beverage3);   //用Mocha装饰
    beverage3 = new Mocha(beverage3);   //用第二个Mocha装饰
    cout << beverage3->getDescription() << " $" << beverage3->cost() << endl;

    delete beverage3;
    delete beverage;

    return 0;
}

欢迎关注公众号:c_302888524
发送:“设计模式:可复用面向对象软件的基础” 获取电子书
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值