【C++】装饰器模式

装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰器模式刚开始看非常难看懂。其实可以想象这种模式如同数据结构的链表,也可以想象成贪吃蛇。

一个类被不停地装饰,相当于在其尾不停地添加结点,最后在读取这个类的时候,犹如读取链表,由于next指针的存在,将会一直回溯读取内容直到next指针为空,从而达到滚雪球的效果。

一个类被装饰得越多,犹如贪吃蛇不停在吃食物导致蛇身越来越长,其内容就越来越丰富。

下面用一道2012年上半年软件设计师的软考题来说明这个例子。

题目是这样的:某咖啡店当卖咖啡时,可以根据顾客的要求在其中加入各种配料,咖啡店会根据所加入的配料来计算费用。咖啡店所供应的咖啡及配料的种类和价格如下表所示。

咖啡有两种:蒸馏咖啡Espresso 25元/杯,深度烘焙咖啡DarkRoast 20元/杯

可以在咖啡里面加配料:摩卡Mocha 10元/份,奶泡Whip 8元/份

现采用装饰器模式Decorator模式来实现计算费用的功能,得到如图5-1所示的类图。


具体实现代码如下所示:

#include <iostream>
#include <string>
using namespace std;
//定义各商品的价格
const int ESPRESSO_PRICE = 25;
const int DRAKROAST_PRICE = 20;
const int MOCHA_PRICE = 10;
const int WHIP_PRICE = 8;

class Beverage{//饮料
protected:
	string description;
public:
	virtual string getDescription(){//这里virtual不能省,不然找不到上一级的Beverage的了
		return description;
	}
	virtual int cost()=0;
};

class CondimentDecorator : public Beverage{//配料
protected:
	Beverage *beverage;//这个指针,用于回溯之前已经存在的内容
};

//两种咖啡,其构造函数皆没有Beverage *beverage;也就是说,这两个类只能作为基础,不能作为叠加上去的配件。
class Espresso : public Beverage{//蒸馏咖啡
public:
	Espresso(){
		description="Espresso"; 
	}
	int cost(){
		return ESPRESSO_PRICE;
	}
};
class DarkRoast : public Beverage {//深度烘焙咖啡
public:
	DarkRoast(){ 
		description = "DardRoast"; 
	}
	int cost(){ 
		return DRAKROAST_PRICE; 
	}
};

//对比起上述两种“基类”,这两种东西构造函数皆存在Beverage *beverage;(Beverage *beverage;的声明源于对CondimentDecorator这个类的继承),其可以叠加
class Mocha : public CondimentDecorator {//摩卡
public:
	Mocha(Beverage* beverage){ 
		this->beverage=beverage; 
	}
	string getDescription(){ 
		return beverage->getDescription()+",Mocha"; 
	}
	int cost(){ 
		return MOCHA_PRICE+beverage->cost(); 
	}
};


class Whip :public CondimentDecorator {//奶泡
public:
	Whip(Beverage* beverage){
		this->beverage=beverage; 
	}
	string getDescription() {
		return beverage->getDescription()+",Whip"; 
	}
	int cost() { 
		return WHIP_PRICE+beverage->cost(); 
	}
};

//主函数
int main() {
	Beverage* beverage = new DarkRoast();
	beverage=new Mocha(beverage);
	beverage=new Whip(beverage);
	cout<<beverage->getDescription()<<"¥"<<beverage->cost()<<endl;
	return 0;
}

运行结果也正如题目要求的编译运行上述程序,其输出结果为:DarkRoast, Mocha, Whip¥38


上面的代码虽然不长,但极具深度。信息量极大。

先从主函数入手,相当于一个客人买了深度烘焙咖啡DarkRoast这种咖啡,然后为了喝得更爽,在里面加了摩卡Mocha与奶泡Whip,最后一共38元结账。

(1)主函数中,之所以一个beverage->getDescription()就能打印出三种东西,是因为,在新建奶泡Whip的时候,主函数的beverage类中已经包含了摩卡Mocha与深度烘焙咖啡DarkRoast。因为你在new摩卡Mocha的时候,你将深度烘焙咖啡DarkRoast扔到摩卡Mocha这个构造函数里面,然后摩卡Mocha自身的beverage指针指向深度烘焙咖啡DarkRoast。最后形成了Whip->"beverage"=Whip->Mocha->"beverage"=Whip->Mocha->DarkRoast的效果,beverage->getDescription()便能够一次打印这三个东西,价格同理,实质上DarkRoast、Mocha、Whip三个类就叠加起来了,DarkRoast被Mocha、Whip所装饰了。

不知道这样说,大家是否已经明白了。还可以想象成一条贪吃蛇,吃了DarkRoast之后又吃了Mocha、Whip,蛇身就从1变成了3。

(2)在class Beverage{}中virtual string getDescription(){}必须为主函数。因为这个函数其子类,不断地在同名方法重写,这时根据C++的特性,这个函数最后定义为虚函数,否则由于编译、引用的关系,其子类的这个同名方法,指针很可能指到父类去,亲测去掉后程序是错误的。反正一个函数被多态,加上虚函数就正确了,具体的理论,我也看得不太懂。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值