定义
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
要点
1)装饰者和被装饰者有相同的超类,是利用继承达到“类型匹配”,而不是利用继承获得“行为”。
2)可以用一个或多个装饰者包装一个对象。
3)因为装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装者)的场合,可以用装饰过的对象代替它。
4)装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。
5)对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。
6)装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂。
类图
设计原则
类应该对扩展开放,对修改关闭。
1)此设计原则的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。这样的设计具有弹性,可以应对改变,可以接受新的功能来应对改变的需求。
2)需要把注意力集中在设计中最有可能改变的地方,然后应用开放-关闭原则。不要把设计的每个部分都这么做。
示例
下面实现咖啡馆点餐系统。不同客人对咖啡的喜好个不相同,有的喜欢加摩卡,有的喜欢加奶泡,咖啡店需要根据客户对咖啡添加品的不同计算出不同的价格。
Beverage.h
#ifndef BEVERAGE_H
#define BEVERAGE_H
#include <string>
namespace starbuzz
{
using std::string;
// 饮料超类
class Beverage
{
public:
Beverage() {}
virtual ~Beverage() {}
virtual int getCost() = 0;
virtual string getDescription() = 0;
};
}
#endif
Coffee.h
#ifndef COFFEE_H
#define COFFEE_H
#include "Beverage.h"
namespace starbuzz
{
// 咖啡类
class Coffee: public Beverage
{
public:
Coffee(string str, int p) : description(str), cost(p)
{
}
~Coffee() {}
int getCost()
{
return cost;
}
string getDescription()
{
return description;
}
private:
string description;
int cost;
};
}
#endif
CondimentDecorator.h
#ifndef CONDIMENT_DECORATOR_H
#define CONDIMENT_DECORATOR_H
#include "Beverage.h"
namespace starbuzz
{
// 装饰者超类,继承自Beverage是为了装饰者和被装饰者有相同的超类
class CondimentDecorator: public Beverage
{
public:
CondimentDecorator() {}
~CondimentDecorator() {}
};
}
#endif
CondimentMocha.h
#ifndef CONDIMENT_MOCHA_H
#define CONDIMENT_MOCHA_H
#include <string>
#include "Beverage.h"
#include "CondimentDecorator.h"
namespace starbuzz
{
// 摩卡装饰者
class CondimentMocha: public CondimentDecorator
{
public:
CondimentMocha(Beverage *b): cost(3), description("Mocha")
{
this->beverage = b;
}
~CondimentMocha() {}
int getCost()
{
// 包装新的价格
return beverage->getCost() + this->cost;
}
string getDescription()
{
// 包装新的描述
return this->description + " " + beverage->getDescription();
}
private:
int cost;
string description;
Beverage *beverage;
};
}
#endif
CondimentWhip.h
#ifndef CONDIMENT_WHIP_H
#define CONDIMENT_WHIP_H
#include <string>
#include "Beverage.h"
#include "CondimentDecorator.h"
namespace starbuzz
{
// 奶泡装饰者
class CondimentWhip: public CondimentDecorator
{
public:
CondimentWhip(Beverage *b): cost(10), description("Whip")
{
this->beverage = b;
}
~CondimentWhip() {}
int getCost()
{
// 包装新的价格
return beverage->getCost() + this->cost;
}
string getDescription()
{
// 包装新的描述
return this->description + " " + beverage->getDescription();
}
private:
int cost;
string description;
Beverage *beverage;
};
}
#endif
main.cpp
#include <iostream>
#include "Beverage.h"
#include "Coffee.h"
#include "CondimentDecorator.h"
#include "CondimentMocha.h"
#include "CondimentWhip.h"
using std::cout;
using std::endl;
using namespace starbuzz;
int main()
{
Beverage *beverage = new Coffee("Coffee", 3); // 咖啡
Beverage *mocha = new CondimentMocha(beverage); // 加入摩卡
Beverage *whip = new CondimentWhip(mocha); // 加入奶泡
cout << beverage->getDescription() << ":" << beverage->getCost() << endl;
cout << mocha->getDescription() << ":" << mocha->getCost() << endl;
cout << whip->getDescription() << ":" << whip->getCost() << endl;
delete whip;
delete mocha;
delete beverage;
return 0;
}
Makefile
CXX = g++
CFLAGS = -Wall
LDFLAGS =
target = res
srcs = main.cpp
objs = $(srcs:.cpp=.o)
.PHONY: all
all: $(target)
$(target): $(objs) FORCE
$(CXX) $(LDFLAGS) -o $(target) $(srcs)
$(objs):%.o:%.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
.PHONY: FROCE
FORCE:
clean:
rm -f $(target) *.o
测试
测试结果如下图所示: