1. 程式人生 > >C++ 學習筆記(26)Decorator Pattern

C++ 學習筆記(26)Decorator Pattern

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 * ( \binom{30}{0} + \binom{30}{1} + \binom{30}{2}...... + \binom{30}{29} + \binom{30}{30} )種子類。

(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;
}