C++ 学习笔记(26)Decorator Pattern

版权声明:如若转载,注明出处即可 https://blog.csdn.net/nishisiyuetian/article/details/82840287

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

猜你喜欢

转载自blog.csdn.net/nishisiyuetian/article/details/82840287