C++ 學習筆記(26)Decorator Pattern
阿新 • • 發佈:2018-12-11
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 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;
}