一、意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator比生成子类更加灵活。
人话:我们希望对基类创建的部分对象添加某些功能,这时候使用装饰模式比创建多个子类更方便。
二、实际问题
现在我们有一个基类产品:奶茶。现在我们有多种添加物:珍珠、椰果、布丁、红豆等。
为了实现多种添加物的自由组合,我们现在又两种方式:(1)使用继承的方式,继承出多种不同的组合子类;(2)将添加物作为奶茶的成员,使用组合的方式完成。使用继承方式的缺点很明显:会有很多子类。使用组合方式来带的最大问题是:将会产生很长的构造函数参数。
下面我们引入装饰模式。
三、UML
四、实例
下面我们来看一个实例:现在我们有一个产品——奶茶,以及两个添加物:珍珠和椰果。现在我们希望能自定义的为不同奶茶对象添加椰果和珍珠,同时我们希望能查看添加物以及添加后的总价格。
我们选择一个抽象产品Imilktea作为基类,派生出具体产品类:Milktea、装饰类Decorator,同时装饰类派生出两个具体的装饰物:珍珠Bubble和椰果Coconut。
#include <iostream>
/* Visual Component */
class Imilktea
{
protected:
int m_price;
public:
Imilktea(const int& p_price = 10)
{
m_price = p_price;
}
virtual void show() const = 0;
virtual int price() const = 0;
};
class Milktea : public Imilktea
{
public:
Milktea(const int& p_price = 10) : Imilktea(p_price) {}
void show() const override
{
std::cout << "Milktea" << std::endl;
}
int price() const override
{
return m_price;
}
};
class Decorator : public Imilktea
{
protected:
Imilktea* m_Component;
public:
Decorator(Imilktea* p, const int& p_price) : Imilktea(p_price)
{
m_Component = p;
}
void show() const override
{
this->m_Component->show();
}
int price() const override
{
return this->m_Component->price();
}
};
/* Concrete Decorator : 珍珠 */
class Bubble : public Decorator
{
private:
int m_DecPrice; // 添加物单价
public:
Bubble(Imilktea* p, const int& p_DecPrice) : Decorator(p, p->price())
{
m_DecPrice = p_DecPrice;
}
void show() const override
{
std::cout << "Bubble" << std::endl;
this->m_Component->show();
}
int price() const override
{
return m_DecPrice + this->m_Component->price();
}
};
/* Concrete Decorator : 椰果 */
class Coconut : public Decorator
{
private:
private:
int m_DecPrice; // 添加物单价
public:
Coconut(Imilktea* p, const int& p_DecPrice) : Decorator(p, p->price())
{
m_DecPrice = p_DecPrice;
}
void show() const override
{
std::cout << "Coconut" << std::endl;
this->m_Component->show();
}
int price() const override
{
return m_DecPrice + this->m_Component->price();
}
};
void Client(Imilktea* milktea)
{
std::cout << "奶茶的成分:" << std::endl;
milktea->show();
std::cout << "奶茶的价格:" << milktea->price() << std::endl;
}
int main()
{
/* 逐层装饰 */
Imilktea* baseMilktea = new Milktea(10); // 基本奶茶
Imilktea* mtAddBubble = new Bubble(baseMilktea, 5); // 加珍珠
Client(mtAddBubble);
Imilktea* mtAddBubbleAddCoconut = new Coconut(mtAddBubble, 3); // 加珍珠、椰果
Client(mtAddBubbleAddCoconut);
Imilktea* mtAddDoubleBubble = new Bubble(mtAddBubble, 5); // 加两分珍珠
Client(mtAddDoubleBubble);
delete baseMilktea, mtAddBubble, mtAddBubbleAddCoconut, mtAddDoubleBubble;
return 0;
}