问题需求:
完成某咖啡店的订单系统,不同的咖啡(综合、深焙、低咖啡因,浓缩)有不同的价格,不同的调料(牛奶、摩卡、豆浆、奶泡)也有不同的价格。根据所加入的调料收 取不同的费用。显然不能用咖啡和调料组合的方法进行穷举订单类型,因为订单类型太多,而且以后还会有新的咖啡出现,也不利于维护和扩展。
装饰者模式:
动态地将责任附加到对象上,若要扩展,装饰者提供了比继承更优弹性的替代方案。
设计思路:
1.装饰者和被装饰者应该为同一类型,即继承与同一个基类Breverage。
2.被装饰者继承基类,并完成接口实现。装饰者继承与一个装饰者基类,该基类与派生出的被装饰者类继承于同一个基类Breverage。装饰者基类的作用可以为装饰者增 加新的功能,而不需要修改Breverage(个人观点)。
3.一个被装饰者可以被多个装饰者装饰。
代码实现:
breverage.h
#ifndef BEVERAGE_H_
#define BEVERAGE_H_
#include<string>
using namespace std;
enum SIZE{ TALL = 0, GRANDE,VENTI};
class Beverage{
public:
virtual string getDescription(){
return description;
}
virtual double cost() = 0;;
virtual SIZE getSize() = 0;
private:
string description = "Unknown Beverage";
SIZE cup_size;
};
#endif
被装饰者(咖啡)类Concrete.h
#ifndef CONCRETE_H_
#define CONCRETE_H_
#include"Beverage.h"
class Espresso :public Beverage{
public:
~Espresso(){
}
Espresso(SIZE size){
description = "Espresso";
cup_size = size;
}
string getDescription()
{
return description;
}
double cost(){
return 1.99;
}
void setSize(SIZE i){//设置咖啡杯的大小
cup_size = i;
}
SIZE getSize(){//获得咖啡杯的大小
return cup_size;
}
private:
string description;
SIZE cup_size;
};
class HouseBlend :public Beverage{
public:
~HouseBlend(){
}
HouseBlend(SIZE size){
description = "House Blend Coffee";
cup_size = size;
}
string getDescription()
{
return description;
}
double cost(){
return 0.89;
}
void setSize(SIZE i){//设置咖啡杯的大小
cup_size = i;
}
SIZE getSize(){//获得咖啡杯的大小
return cup_size;
}
private:
string description;
SIZE cup_size;
};
#endif
装饰者(调料)类 Condiment.h。增加了根据咖啡杯大小决定调料价格的功能
#ifndef CONDIMENT_H_
#define CONDIMENT_H_
#include"Beverage.h"
class Condiment :public Beverage{
public:
virtual string getDescription()=0;
};
//摩卡
class Mocha :public Condiment{
public:
Mocha(){
}
~Mocha(){
}
Mocha(Beverage *temp){
this->coffee = temp;
}
string getDescription(){
return coffee->getDescription() + ", Mocha";
}
SIZE getSize(){
return coffee->getSize();
}
double cost(){//根据咖啡杯的大小决定加摩卡的价格
double cost = coffee->cost();
switch (getSize())
{
case TALL:
cost += 0.20;
break;
case GRANDE:
cost += 0.25;
break;
case VENTI:
cost += 0.30;
break;
default:cout << "There is not this SIZE" << endl;
}
return cost;
}
private:
Beverage *coffee;
};
//豆浆
class Soy :public Condiment{
public:
Soy(){}
~Soy(){}
Soy(Beverage *temp){
this->coffee = temp;
}
string getDescription(){
return coffee->getDescription() + ", Soy";
}
SIZE getSize(){
return coffee->getSize();
}
double cost(){//根据咖啡杯的大小决定加豆浆的价格
double cost = coffee->cost();
switch (getSize())
{
case TALL:
cost += 0.15;
break;
case GRANDE:
cost += 0.20;
break;
case VENTI:
cost += 0.25;
break;
default:cout << "There is not this SIZE" << endl;
}
return cost;
}
private:
Beverage *coffee;
};
//奶泡
class Whip :public Condiment{
public:
Whip(){
}
~Whip(){
}
Whip(Beverage *temp){
this->coffee = temp;
}
string getDescription(){
return coffee->getDescription() + ", Soy";
}
SIZE getSize(){
return coffee->getSize();
}
double cost(){
double cost = coffee->cost();
switch (getSize())
{
case TALL:
cost += 0.10;
break;
case GRANDE:
cost += 0.15;
break;
case VENTI:
cost += 0.20;
break;
default:cout << "There is not this SIZE" << endl;
}
return cost;
}
private:
Beverage *coffee;
};
#endif
主函数main.cpp
#include<iostream>
#include"Beverage.h"
#include"Concrete.h"
#include"Condiment.h"
using namespace std;
int main()
{
Beverage *beverage1 = new Espresso(TALL);
cout << beverage1->getDescription() << " $" << beverage1->cost() << endl;
Beverage *beverage2 = new HouseBlend(GRANDE);
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);//用奶泡,2份摩卡“装饰”中杯的HouseBlend
cout << beverage2->getDescription() << " $" << beverage2->cost() << endl;
return 0;
}
运行结果:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN0LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX9EFRNRTUE9ENnRlT6VEVZZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jM0UzMxMTN1EDNxEDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
总结:
1.装饰者和被装饰者必须是同一类型,这是装饰者模式的关键,在这里是利用继承达到“类型匹配”,而非继承行为。
2.装饰者和被装饰者进行组合时,就在装饰者中加入了新的行为,行为不是来自继而是来自组合。
3.依赖继承的行为,在编译时静态决定。利用组合,可以把装饰者混合使用,并且是在运行时决定行为。
感觉理解还不是很透彻,先写这些吧。