Decorator Pattern
装饰者模式
假设现在要对一个已有的类进行功能拓展,思路可以选用继承。但是如果要拓展的功能比较多,而且功能之间可以随意搭配,例如两种主材料 A, B, C, 可以搭配的材料有 X, Y, Z ,如果继续采用继承,就大致如下:
// -----------------------------------------
class Meterial {} ;
class Meterial_A_X : public Meterial {} ;
class Meterial_A_Y : public Meterial {} ;
class Meterial_A_Z : public Meterial {} ;
class Meterial_B_X : public Meterial {} ;
class Meterial_B_Y : public Meterial {} ;
class Meterial_B_Z : public Meterial {} ;
但是,如果材料越来越多,需要定义的继承类也越来越多。
所以,可以考虑组合——装饰者模式
1. 定义:创建一个包装对象,包裹真实的对象,避开继承,使用组合,以拓展类的功能。
2. 设计:主要分为两大类——真实对象,装饰对象,模式如下:
// ----------------- 真实类 ----------------
class Meterial {} ;
class Meterial_A : public Meterial {} ;
class Meterial_B : public Meterial {} ;
// ----------------- 装饰类 ----------------
class Decorator : public Meterial {} ;
class Decorator_X : public Decorator {
private:
std::shared_ptr<Meterial> ptr;
} ;
class Decorator_Y : public Decorator {
private:
std::shared_ptr<Meterial> ptr;
} ;
class Decorator_Z : public Decorator {
private:
std::shared_ptr<Meterial> ptr;
} ;
现在可以对比一下效果,如果主材料有 10 种,搭配材料有 30 种。
(1)采用继承,考虑所有组合,需要定义 10 * ( )种子类。
(2)采用装饰者模式,只需要定义 10 + 30 = 40 个子类。
采用装饰者模式很适用于那些大量拓展功能的情况。
具体效果如下:
------------- 酸甜搭配 -------------
酸甜烤冷面 : 5
酸甜烤冷面 + 烤肠 : 6
酸甜烤冷面 + 培根 : 7
酸甜烤冷面 + 鸡柳 : 8
酸甜烤冷面 + 烤肠 + 鸡柳 : 9
------------- 香辣搭配 -------------香辣烤冷面 : 6
香辣烤冷面 + 烤肠 : 7
香辣烤冷面 + 培根 : 8
香辣烤冷面 + 鸡柳 : 9
香辣烤冷面 + 烤肠 + 鸡柳 : 10--------------- 撤销 ? ---------------
酸甜烤冷面 + 烤肠 : 6
--------------- 替换 ? ---------------香辣烤冷面 + 烤肠 : 7
香辣烤冷面 + 鸡柳 : 9
代码源自网络:
#include <iostream>
#include <memory>
namespace Stall {
// ----------------- 真实类 ----------------
class Meterial {
public:
virtual ~Meterial() = default;
virtual double getCost() const = 0;
virtual std::string getName() const = 0;
virtual void reGet(std::shared_ptr<Meterial> _ptr) = 0;
} ;
using ptrType = std::shared_ptr<Meterial>;
inline void display(ptrType ptr) {
std::cout << ptr->getName() << " : " << ptr->getCost() << std::endl;
}
class Meterial_A : public Meterial {
private:
double cost = 5.00;
std::string name = "酸甜烤冷面";
public:
virtual double getCost() const {
// 薄记等其他工作
return cost;
}
virtual std::string getName() const {
// 薄记等其他工作
return name;
}
virtual void reGet(std::shared_ptr<Meterial> _ptr) {}
} ;
class Meterial_B : public Meterial {
private:
double cost = 6.00;
std::string name = "香辣烤冷面";
public:
virtual double getCost() const {
// 薄记等其他工作
return cost;
}
virtual std::string getName() const {
// 薄记等其他工作
return name;
}
virtual void reGet(std::shared_ptr<Meterial> _ptr) {}
} ;
// ----------------- 装饰类 ----------------
class Decorator : public Meterial {
public:
virtual void reGet(std::shared_ptr<Meterial> _ptr) {};
} ;
class Decorator_X : public Decorator {
private:
ptrType ptr;
std::string name = " + 烤肠";
public:
Decorator_X(ptrType _ptr) : ptr (_ptr) {}
virtual double getCost() const {
// 薄记等其他工作
return ptr->getCost() + 1.00;
}
virtual std::string getName() const {
// 薄记等其他工作
return ptr->getName() + name; // RVO
}
virtual void reGet(ptrType _ptr) {
// 合法性判断, 逻辑等
ptr = _ptr;
}
} ;
class Decorator_Y : public Decorator {
private:
ptrType ptr;
std::string name = " + 培根";
public:
Decorator_Y(ptrType _ptr) : ptr (_ptr) {}
virtual double getCost() const {
// 薄记等其他工作
return ptr->getCost() + 2.00;
}
virtual std::string getName() const {
// 薄记等其他工作
return ptr->getName() + name; // RVO
}
virtual void reGet(ptrType _ptr) {
// 合法性判断, 逻辑等
ptr = _ptr;
}
} ;
class Decorator_Z : public Decorator {
private:
ptrType ptr;
std::string name = " + 鸡柳";
public:
Decorator_Z(ptrType _ptr) : ptr (_ptr) {}
virtual double getCost() const {
// 薄记等其他工作
return ptr->getCost() + 3.00;
}
virtual std::string getName() const {
// 薄记等其他工作
return ptr->getName() + name; // RVO
}
virtual void reGet(ptrType _ptr) {
// 合法性判断, 逻辑等
ptr = _ptr;
}
} ;
}
int main() {
using namespace Stall;
std::cout << "\n\n------------- 酸甜搭配 -------------\n\n";
// ---------------- 酸甜烤冷面 -----------------
ptrType mainA = std::make_shared<Meterial_A>();
display(mainA);
// ---------------- 酸甜烤冷面 + 烤肠 -----------------
ptrType mixX = std::make_shared<Decorator_X>(mainA);
display(mixX);
// ---------------- 酸甜烤冷面 + 培根 -----------------
ptrType mixY = std::make_shared<Decorator_Y>(mainA);
display(mixY);
// ---------------- 酸甜烤冷面 + 鸡柳 -----------------
ptrType mixZ = std::make_shared<Decorator_Z>(mainA);
display(mixZ);
// ---------------- 酸甜烤冷面 + 烤肠 + 培根 -----------------
ptrType mixXY = std::make_shared<Decorator_Z>(mixX);
display(mixXY);
std::cout << "\n\n------------- 香辣搭配 -------------\n\n";
// ---------------- 香辣烤冷面 -----------------
ptrType mainB = std::make_shared<Meterial_B>();
display(mainB);
// ---------------- 香辣烤冷面 + 烤肠 -----------------
ptrType mixX2 = std::make_shared<Decorator_X>(mainB);
display(mixX2);
// ---------------- 香辣烤冷面 + 培根 -----------------
ptrType mixY2 = std::make_shared<Decorator_Y>(mainB);
display(mixY2);
// ---------------- 香辣烤冷面 + 鸡柳 -----------------
ptrType mixZ2 = std::make_shared<Decorator_Z>(mainB);
display(mixZ2);
// ---------------- 香辣烤冷面 + 烤肠 + 培根 -----------------
ptrType mixXY2 = std::make_shared<Decorator_Z>(mixX2);
display(mixXY2);
// 动态撤销 ?
std::cout << "\n--------------- 撤销 ? ---------------\n\n";
display(mixX);
// 替换 ?
std::cout << "\n\n--------------- 替换 ? ---------------\n\n";
mixX->reGet(mainB);
display(mixX);
mixXY->reGet(mainB); // 尚未实现......
display(mixXY);
return 0;
}