24种设计模式的C++实现(万字长文,欢迎收藏)

原文链接

目录

为什么使用设计模式

性能提升

单例模式

享元模式

对象创建模式组

简单工厂模式

抽象工厂模式

建造者模式

接口隔离模式组

facade(外观)模式

代理者模式

适配器模式

中介者模式

组件协作模式

策略模式

观察者模式

单一职责模式组

装饰器模式

桥接模式

行动变化模式组

命令模式

访问者模式

状态变化模式组

状态模式

备忘录模式

解释器模式

数据结构模式组

迭代器模式

组合模式

责任链模式


为什么使用设计模式

抵御变化复用代码

性能提升

单例模式

单例模式的定义

确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例。

方法一:

static Singleton *getInstance()
    {
        static Singleton locla_s;
        return &locla_s;
    }

该代码可能在c++11之前的版本导致多次构造函数的调用,所以只能在较新的编译器上使用。

方法二:

static Singleton *getInstance()
    {
        pthread_mutex_lock(&mutex);
        if (local_instance == nullptr)
        {
            local_instance = new Singleton();
        }
        pthread_mutex_unlock(&mutex);
        return local_instance;
    }

下面这个版本使用了mutex以及静态成员来析构单例。该方案的劣处在于锁导致速度慢,效率低。但是至少是正确的,也能在c++11之前的版本使用。

方法三:

static Singleton *getInstance()
    {
        if(local_instance == nullptr){
            pthread_mutex_lock(&mutex);
            if (local_instance == nullptr)
            {
                local_instance = new Singleton();
            }
            pthread_mutex_unlock(&mutex);
        }
        return local_instance;
    }

使用双锁检查导致未初始化的内存访问

使用如下的代码来实现已经初始化的对象的直接返回。可以使上述代码性能会大大加快。但是相同的代码在Java下面有很明显的问题,由于CPU乱序执行,可能导致访问到未经初始化的对象的引用。c++也存在相同的问题,可能导致未定义行为导致段错误

假如线程A进入锁内并分配对象的空间,但是由于指令可能乱序,实际上导致local_instance被先指向一块未被分配的内存,然后再在这块内存上进程初始化。但是在指向后,未初始化前,另一线程B可能通过getInstance获取到这个指针。

方法四

在新的标准中,atomic类实现了内存栅栏,使得多个核心访问内存时可控。这利用了c++11的内存访问顺序可控。

#include 
#include 
#include 
#include 

using namespace std;
class Singleton
{
private:
	static atomic instance;

	class rememberFree {
	public:
		rememberFree() {}
		~rememberFree() {
			Singleton* local_instance = 
   instance.load(std::memory_order_relaxed);
			if (local_instance != nullptr) {
				delete local_instance;
			}
		}
	};
	static rememberFree remember;

public:
	static Singleton *getInstance()
	{
		Singleton* tmp = instance.load(std::memory_order_relaxed);
		atomic_thread_fence(memory_order_acquire);
		if (tmp == nullptr) {
			static mutex mtx;
			lock_guard lock(mtx);
			tmp = instance.load(memory_order_relaxed);
			if (tmp == nullptr)
			{
				tmp = new Singleton();
				atomic_thread_fence(memory_order_release);
				instance.store(tmp, memory_order_relaxed);
			}
		}
		return tmp;
	}
};

atomic Singleton::instance;
Singleton::rememberFree Singleton::remember;

int main()
{
	Singleton * s2 = Singleton::getInstance();
}

RAII方式同步线程锁

通过C++的这种机制,我们可以很方便地处理C++中的加锁同步机制。把锁对象作为Guard对象的一个成员(m_lock),然后在Guard对象的构造中对m_lock进行加锁:m_lock.acquire(),在Guard对象的析构函数中进行解锁:m_lock.release()

template 
class Guard
{
public :
        Guard(const T & lock);
        virtual ~Guard();
private:
        const T & m_lock;
};
template 
Guard::Guard(const T & lock) :
        m_lock(lock)
{
        m_lock.acquire();
}
template 
Guard::~Guard()
{
    m_lock.release();
}
我们可以在应用程序中这样使用它:
 void testFunc(.....)
{
  Guard  guard(mutex);
  ...
}

享元模式

运用共享技术有效的支持大量细粒度的对象

#include 
#include 

using namespace std;

class Font {
private:
	string key;
public:
	Font(const string& key) {
	}
};

class FontFactory {
private:
	map fontPool;

public:
	Font* GetFont(const string& key) {
		map::iterator item = fontPool.find(key);
		if (item != fontPool.end) {
			return fontPool[key];
		}
		else {
			Font* font = new Font(key);
			fontPool[key] = font;
			return font;
		}
	}
	void clear() {
	}
};

享元模式的优缺点

享元模式是一个非常简单的模式, 它可以大大减少应用程序创建的对象, 降低程序内存的占用, 增强程序的性能, 但它同时也提高了系统复杂性, 需要分离出外部状态和内部状态, 而且外部状态具有固化特性, 不应该随内部状态改变而改变, 否则导致系统的逻辑混乱。

享元模式使用场景

● 系统中存在大量的相似对象。

● 细粒度的对象都具备较接近的外部状态, 而且内部状态与环境无关, 也就是说对象没

有特定身份。

● 需要缓冲池的场景

对象创建模式组

简单工厂模式

//抽象类
enum SpliterType {
	BinaryType = 0,
	TxtYype = 1
};

class ISplitter {
public:
	virtual void split() = 0;
	virtual ~ISplitter() {}
};
//具体类
class BinarySplitter : public ISplitter {
public:
	void split() {};
};

class TxtSplitter : public ISplitter {
public:
	void split(){}
};

//工厂基类
class SplitterFactory {
public:
	static ISplitter* CreateSplitter(SpliterType type)
	{
		switch (type)
		{
		case BinaryType:
			return new BinarySplitter();
			break;
		case TxtYype:
			return new TxtSplitter();
			break;
		default:
			break;
		}
	}
};

抽象工厂模式

//数据库访问有关的基类
class IDBConnection{};
class IDBCommand{};
class IDataReader{};

class IDBFactory{
public:
    virtual IDBConnection* CreateDBConnection()=0;
    virtual IDBCommand* CreateDBCommand()=0;
    virtual IDataReader* CreateDataReader()=0;
    
};

//支持SQL Server
class SqlConnection: public IDBConnection{ };
class SqlCommand: public IDBCommand{ };
class SqlDataReader: public IDataReader{ };

class SqlDBFactory:public IDBFactory{
public:
    virtual IDBConnection* CreateDBConnection()=0;
    virtual IDBCommand* CreateDBCommand()=0;
    virtual IDataReader* CreateDataReader()=0; 
};

建造者模式

建造者模式的优点:

封装性

建造者独立,易于扩展

便于控制细节风险

建造者模式使用的场景:

相同的方法, 不同的执行顺序, 产生不同的事件结果时, 可以采用建造者模式多个部件或零件, 都可以装配到一个对象中, 但是产生的运行结果又不相同时, 则可以使用该模式。产品类非常复杂, 或者产品类中的调用顺序不同产生了不同的效能, 这个时候使用建造者模式非常合适。

class House{};
class HouseBuilder {
public:
    House* GetResult(){
        return pHouse;
    }
    virtual ~HouseBuilder(){}
protected:
    
    House* pHouse;
	virtual void BuildPart1()=0;
    virtual void BuildPart2()=0;
    virtual void BuildPart3()=0;
	
};
class StoneHouse: public House{};
class StoneHouseBuilder: public HouseBuilder{
protected:
    
    virtual void BuildPart1(){}
    virtual void BuildPart2(){}
    virtual void BuildPart3(){}   
};
class HouseDirector{    
public:
    HouseBuilder* pHouseBuilder;  
    HouseDirector(HouseBuilder* pHouseBuilder){
        this->pHouseBuilder=pHouseBuilder;
    }    
    House* Construct(){    
			pHouseBuilder->BuildPart1();			
			pHouseBuilder->BuildPart2();
			bool flag=pHouseBuilder->BuildPart3();
        return pHouseBuilder->GetResult();
    }
};

接口隔离模式组

facade(外观)模式

为系统中一组接口提供一个(稳定)的界面,Facade定义了一个高层接口,这个接口使得子系统更加容易复用。

门面模式的优点:

减少系统的相互依赖

提高了灵活性

提高安全性

门面模式的缺点:

门面模式最大的缺点就是不符合开闭原则, 对修改关闭, 对扩展开放, 看看我们那个门面对象吧, 它可是重中之重, 一旦在系统投产后发现有一个小错误, 你怎么解决? 完全遵从开闭原则, 根本没办法解决。 继承? 覆写? 都顶不上用, 唯一能做的一件事就是修改门面角色的代码, 这个风险相当大, 这就需要大家在设计的时候慎之又慎, 多思考几遍才会有好收获。

门面模式的应用场景:

1. 为一个复杂的模块或子系统提供一个供外界访问的接口

2. 子系统相对独立——外界对子系统的访问只要黑箱操作即可

3. 预防低水平人员带来的风险扩散

class ClassA {
public:
		void doSomethingA() {
		//业务逻辑
	}
};
class ClassB {
public:
	void doSomethingB() {
		//业务逻辑
	}
};
class ClassC {
public:
	void doSomethingC() {
		//业务逻辑
	}
};

class Facade {
	//被委托的对象
	ClassA* a = new ClassA();
	ClassB* b = new ClassB();
	ClassC* c = new ClassC();

	//提供给外部访问的方法
	void methodA() {
		a->doSomethingA();
	}
	void methodB() {
		b->doSomethingB();
	}
	void methodC() {
		c->doSomethingC();
	}
}

代理者模式

模式定义

为其他对象提供一种代理控制(隔离,使用接口)对这个对象进行访问。

class ISubject {
public:
	virtual void process(){}
};
//Proxy的设计
class SubjectProxy:public ISubject {
public:
	virtual void process() {
		//对RealSubject的一种间接访问
	}
};
class ClientApp {
	ISubject* subject;
public:
	ClientApp() {
		subject = new SubjectProxy();
	}
	void DoTask() {
		subject->process();
	}
};
class ISubject {
public:
	virtual void process();
};
class RealSubject : public ISubject {
public:
	virtual void process() {
		//....
	}
};

适配器模式

模式定义

将一类的接口转换成客户希望的另一个接口,Adaptor模式使得原本由于接口不兼容而不能工作的那些类可以一起工作。

适配器模式的优点

适配器模式可以让两个没有任何关系的类在一起运行, 只要适配器这个角色能够搞定他们就成。

增加了类的透明性

提高了类的复用程度

灵活性非常好

适配器模式的使用场景

你有动机修改一个已经投产中的接口时, 适配器模式可能是最适合你的模式。

适配器模式最好在详细设计阶段不要考虑它, 它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题, 没有一个系统分析师会在做详细设计的时候考虑使用适配器模式, 这个模式使用的主要场景是扩展应用中。

//目标接口(新接口)
class ITarget {
public:
	virtual void process() = 0;
};

//遗留接口(老接口)
class IAdaptee {
public:
	virtual void foo(int data) = 0;
	virtual int bar() = 0;
};
//遗留类型
class OldClass : public IAdaptee {
	void foo(int data){}
	int bar(){}
};
//对象适配器
class Adapter : public ITarget { //继承
protected:
	IAdaptee* pAdaptee;//组合
public:
	Adapter(IAdaptee* pAdaptee) {
		this->pAdaptee = pAdaptee;
	}
	virtual void process() {
		int data = pAdaptee->bar();
		pAdaptee->foo(data);
	}
};
int main() {
	IAdaptee* pAdaptee = new OldClass();
	ITarget* pTarget = new Adapter(pAdaptee);
	pTarget->process();
}

中介者模式

模式定义

用一个中介对象来封装(封装变化)一系列的对象交互。中介者使各对象不需要显式的相互依赖>运行时依赖),从而使其耦合松散(管理变化),而且可以独立地改变它们之间的交互。

中介者模式的优点

中介者模式的优点就是减少类间的依赖, 把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者, 减少了依赖, 当然同时也降低了类间的耦合。

中介者模式的缺点

中介者模式的缺点就是中介者会膨胀得很大, 而且逻辑复杂, 原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系, 同事类越多, 中介者的逻辑就越复杂。

中介者模式的实际应用

机场调度中心

MVC框架

媒体网关

中介服务

//通用抽象中介者
class Mediator {
	//定义同事类
protected:
	ConcreteColleague1 m_c1;
    ConcreteColleague2 m_c2;
public:
	ConcreteColleague1 getC1() {
		return m_c1;
	}
	void setC1(ConcreteColleague1 c1) {
		m_c1 = c1;
	}
	ConcreteColleague2 getC2() {
		return m_c2;
	}
	void setC2(ConcreteColleague2 c2) {
		m_c2 = c2;
	}
	//中介者模式的业务逻辑
	virtual  void doSomething1() = 0;
	virtual  void doSomething2() = 0;
};
//通用中介者
class ConcreteMediator : public Mediator {
public:
	void doSomething1() {
		m_c1.selfMethod1();
		m_c2.selfMethod2();
	}
	void doSomething2() {
		m_c1.selfMethod1();
		m_c2.selfMethod2();
	}
};

//抽象同事类
class Colleague {
protected:
	Mediator* mediator;
public:
	Colleague(Mediator* _mediator) {
		mediator = _mediator;
	}
};
//具体同事类
class ConcreteColleague1:public Colleague {
	//通过构造函数传递中介者
public:
	ConcreteColleague1(Mediator* _mediator):Colleague(_mediator){
	}
public:
	void selfMethod1() {
		//处理自己的业务逻辑
	}
	//依赖方法 dep-method
	void depMethod1() {
		mediator->doSomething1();
	}
};
class ConcreteColleague2:public Colleague {
public:
	ConcreteColleague2(Mediator* _mediator):Colleague(_mediator) {}
public:
	void selfMethod2() {
		//处理自己的业务逻辑
	}
	//依赖方法 dep-method
	void depMethod2() {
		//处理自己的业务逻辑
		//自己不能处理的业务逻辑, 委托给中介者处理
		mediator->doSomething2();
	}
};

组件协作模式

模板模式

模式定义

定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。 Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。

//程序库开发人员
class Library {
public:
	void Run() {
		Step1();
		if (Step2()) { //支持变化 ==> 虚函数的多态调用
			Step3();
		}
		for (int i = 0; i < 4; i++) {
			Step4(); //支持变化 ==> 虚函数的多态调用
		}
		Step5();
	}
	virtual ~Library(){ }
protected:
	void Step1() {}
	void Step3() {}
	void Step5() {}
	virtual bool Step2() = 0;//变化
	virtual void Step4() = 0; //变化
};

策略模式

模式定义

定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。

策略模式的优点

算法可以自由切换

避免使用多重条件判断

扩展性良好

策略模式的缺点

策略数量众多

所有策略算法都需要对外暴露

策略模式的使用场景

多个类只有在算法或行为上稍有不同的场景

算法需要自由切换的场景。

需要屏蔽算法规则的场景。

class Context {};
class TaxStrategy {
public:
	virtual double Calculate(const Context& context) = 0;
	virtual ~TaxStrategy() {}
};

class CNTax : public TaxStrategy {
public:
	virtual double Calculate(const Context& context) {}
};

class USTax : public TaxStrategy {
public:
	virtual double Calculate(const Context& context) {}
};
class StrategyFactory{
	//工厂模式
public:
	TaxStrategy* NewStrategy(){}
};

class SalesOrder {
private:
	TaxStrategy* strategy;
public:
	SalesOrder(StrategyFactory* strategyFactory) {
		this->strategy = strategyFactory->NewStrategy();
	}
	~SalesOrder() {
		delete this->strategy;}
public:
	double CalculateTax() {
		Context context;
		double val = strategy->Calculate(context); 
	}
};

观察者模式

模式定义

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新

要点总结

使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。

目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。

观察者自己决定是否需要订阅通知,目标对象对此一无所知。

Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。

观察者模式的优点

观察者和被观察者之间是抽象耦合

建立一套触发机制

观察者模式的缺点

观察者模式需要考虑一下开发效率和运行效率问题, 一个被观察者, 多个观察者, 开发和调试就会比较复杂, 而且在Java中消息的通知默认是顺序执行, 一个观察者卡壳, 会影响整体的执行效率。 在这种情况下, 一般考虑采用异步的方式。多级触发时的效率更是让人担忧, 大家在设计时注意考虑。

观察者模式的使用场景

● 关联行为场景。 需要注意的是, 关联行为是可拆分的, 而不是“组合”关系。

● 事件多级触发场景。

● 跨系统的消息交换场景, 如消息队列的处理机制。

观察者模式的扩展:

观察者模式也叫做发布/订阅模型(Publish/Subscribe)

#include 
#include 
#include 
#include 

using namespace std;
class IProgress {
public:
	virtual void DoProgress(float value) = 0;
	virtual ~IProgress() {}
};

class FileSplitter
{
	string m_filePath;
	int m_fileNumber;
	list  m_iprogressList; // 抽象通知机制,支持多个观察者
public:
	FileSplitter(const string& filePath, int fileNumber) :
		m_filePath(filePath),
		m_fileNumber(fileNumber) {
	}
	void split() {
		//1.读取大文件
		//2.分批次向小文件中写入
		for (int i = 0; i < m_fileNumber; i++) {
			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			onProgress(progressValue);//发送通知
		}
	}
	void addIProgress(IProgress* iprogress) {
		m_iprogressList.push_back(iprogress);
	}
	void removeIProgress(IProgress* iprogress) {
		m_iprogressList.remove(iprogress);
	}

protected:
	virtual void onProgress(float value) {
		list::iterator itor = m_iprogressList.begin();
		while (itor != m_iprogressList.end())
			(*itor)->DoProgress(value); //更新进度条
		itor++;
	}
};
class MainForm : public Form, public IProgress
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;
public:
	void Button1_Click() {

		string filePath = txtFilePath->getText();
		int number = atoi(txtFileNumber->getText().c_str());

		ConsoleNotifier cn;
		FileSplitter splitter(filePath, number);
		splitter.addIProgress(this); //订阅通知
		splitter.addIProgress(&cn);  //订阅通知
		splitter.split();
		splitter.removeIProgress(this);
	}
	virtual void DoProgress(float value) {
		progressBar->setValue(value);
	}
};
class ConsoleNotifier : public IProgress {
public:
	virtual void DoProgress(float value) {cout << ".";}
};

单一职责模式组

装饰器模式

模式定义

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)

要点解析

通过采用组合而非继承的手法, Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。

Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。

Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

//业务操作
class Stream {
public:
	virtual char Read(int number) = 0;
	virtual void Seek(int position) = 0;
	virtual void Write(char data) = 0;
	virtual ~Stream() {}
};
//主体类
class FileStream : public Stream {
public:
	virtual char Read(int number) {
		//读文件流
	}
	virtual void Seek(int position) {
		//定位文件流
	}
	virtual void Write(char data) {
		//写文件流
	}
};
class NetworkStream :public Stream {
public:
	virtual char Read(int number) {
		//读网络流
	}
	virtual void Seek(int position) {
		//定位网络流
	}
	virtual void Write(char data) {
		//写网络流
	}
};
//扩展操作
class DecoratorStream: public Stream{
protected:
	Stream* stream;
	DecoratorStream(Stream * stm) :stream(stm) {}
};
class CryptoStream : public DecoratorStream {
public:
	CryptoStream(Stream* stm) :DecoratorStream(stm) {
	}
	virtual char Read(int number) {
		//额外的加密操作...
		stream->Read(number);//读文件流
	}
	virtual void Seek(int position) {
		//额外的加密操作...
		stream->Seek(position);//定位文件流
	}
	virtual void Write(char data) {
		//额外的加密操作...
		stream->Write(data);//写文件流
	}
};
void Process() {
	//运行时装配
	FileStream* s1 = new FileStream();
	CryptoStream* s2 = new CryptoStream(s1);
}

桥接模式

模式定义

将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。

要点总结

Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。

Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。

Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。

桥梁模式的优点

抽象和实现相分离

优秀的扩充能力

实现细节对客户透明

桥梁模式的使用场景:

● 不希望或不适用使用继承的场景

例如继承层次过渡、 无法更细化设计颗粒等场景, 需要考虑使用桥梁模式。

● 接口或抽象类不稳定的场景

明知道接口不稳定还想通过实现或继承来实现业务需求, 那是得不偿失的, 也是比较失败的做法。

● 重用性要求较高的场景

设计的颗粒度越细, 则被重用的可能性就越大, 而采用继承则受父类的限制, 不可能出现太细的颗粒度。

class Messager {
protected:
	MessagerImp* messagerImp;
public:
	virtual void Login(string username, string password) = 0;
	virtual void SendMessage(string message) = 0;
	virtual void SendPicture(Image image) = 0;
	virtual ~Messager() {}
};

class MessagerImp {
public:
	virtual void PlaySound() = 0;
	virtual void DrawShape() = 0;
	virtual void WriteText() = 0;
	virtual void Connect() = 0;

	virtual ~MessagerImp() {}
};

//平台实现
class PCMessagerImp : public MessagerImp {
public:

	virtual void PlaySound() {}
	virtual void DrawShape() {}
	virtual void WriteText() {}
	virtual void Connect() {}
};

class MobileMessagerImp : public MessagerImp {
public:

	virtual void PlaySound() {}
	virtual void DrawShape() {}
	virtual void WriteText() {}
	virtual void Connect() {}
};

class MessagerLite :public Messager {
public:
	virtual void Login(string username, string password) {
		messagerImp->Connect();
	}
	virtual void SendMessage(string message) {
		messagerImp->WriteText();
	}
	virtual void SendPicture(Image image) {
		messagerImp->DrawShape();
	}
};

class MessagerPerfect :public Messager {
public:
	virtual void Login(string username, string password) {
		messagerImp->PlaySound();
		messagerImp->Connect();
	}
	virtual void SendMessage(string message) {
		messagerImp->PlaySound();
		messagerImp->WriteText();
	}
	virtual void SendPicture(Image image) {
		messagerImp->PlaySound();
		messagerImp->DrawShape();
	}
};

void Process(){
	MessagerImp* mImp = new PCMessagerImp();
	Messager *m = new MessagerPerfect(mImp);
}

行动变化模式组

命令模式

模式定义

将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排日志,以及支持可撤销的操作。

class Command
{
public:
	virtual void execute() = 0;
};
class ConcreteCommand1 : public Command
{
	string arg;
public:
	ConcreteCommand1(const string & a) : arg(a) {}
	void execute() override
	{
		cout << "#1 process..." << arg << endl;
	}
};
class ConcreteCommand2 : public Command
{
	string arg;
public:
	ConcreteCommand2(const string & a) : arg(a) {}
	void execute() override
	{
		cout << "#2 process..." << arg << endl;
	}
};
class MacroCommand : public Command
{
	vector commands;
public:
	void addCommand(Command *c) { commands.push_back(c);}
	void execute() override
	{
		for (auto &c : commands)
		{
			c->execute();
		}
	}
};
int main()
{
	ConcreteCommand1 command1(receiver, "Arg ###");
	ConcreteCommand2 command2(receiver, "Arg $$$");
	MacroCommand macro;
	macro.addCommand(&command1);
	macro.addCommand(&command2);
	macro.execute();

}

访问者模式

模式定义

表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。

class Visitor;
class Element
{
public:
	virtual void accept(Visitor& visitor) = 0; //第一次多态辨析
	virtual ~Element() {}
};
class ElementA : public Element
{
public:
	void accept(Visitor &visitor){
		visitor.visitElementA(*this);
	}
};
class ElementB : public Element
{
public:
	void accept(Visitor &visitor){
		visitor.visitElementB(*this); //第二次多态辨析
	}
};
class Visitor{
public:
	virtual void visitElementA(ElementA& element) = 0;
	virtual void visitElementB(ElementB& element) = 0;
	virtual ~Visitor() {}
};
class Visitor1 : public Visitor {
public:
	void visitElementA(ElementA& element) override {
		cout << "Visitor1 is processing ElementA" << endl;
	}
	void visitElementB(ElementB& element) override {
		cout << "Visitor1 is processing ElementB" << endl;
	}
};

状态变化模式组

状态模式

模式定义

允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。

状态模式的优点

结构清晰

遵循设计原则

封装性非常好

状态模式的缺点

状态模式既然有优点, 那当然有缺点了。 但只有一个缺点, 子类会太多, 也就是类膨胀。 如果一个事物有很多个状态也不稀奇, 如果完全使用状态模式就会有太多的子类, 不好管理, 这个需要大家在项目中自己衡量。

状态模式的应用场景

● 行为随状态改变而改变的场景

这也是状态模式的根本出发点, 例如权限设计, 人员的状态不同即使执行相同的行为结果也会不同, 在这种情况下需要考虑使用状态模式。

● 条件、 分支判断语句的替代者

在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰, 逻辑混乱, 使用状态模式可以很好地避免这一问题, 它通过扩展子类实现了条件的判断处理。

class NetworkState {
public:
	NetworkState* pNext;
	virtual void Operation1() = 0;
	virtual void Operation2() = 0;
	virtual void Operation3() = 0;
	virtual ~NetworkState() {}
};
class OpenState :public NetworkState {
	static NetworkState* m_instance;
public:
	static NetworkState* getInstance() {
		if (m_instance == nullptr) {
			m_instance = new OpenState();
		}
		return m_instance;
	}
	void Operation1() {
		pNext = CloseState::getInstance();
	}

	void Operation2() {
		pNext = OpenState::getInstance();
	}

	void Operation3() {
		pNext = OpenState::getInstance();
	}
};
class CloseState :public NetworkState {
	static NetworkState* m_instance;
public:
	static NetworkState* getInstance() {
		if (m_instance == nullptr) {
			m_instance = new CloseState();
		}
		return m_instance;
	}
	void Operation1() {
		pNext = CloseState::getInstance();
	}

	void Operation2() {
		pNext = OpenState::getInstance();
	}

	void Operation3() {
		pNext = OpenState::getInstance();
	}
};
class NetworkProcessor {
	NetworkState* pState;
public:
	NetworkProcessor(NetworkState* pState) {
		this->pState = pState;
	}
	void Operation1() {
		pState->Operation1();
		pState = pState->pNext;
	}
	void Operation2() {
		pState->Operation2();
		pState = pState->pNext;
	}
	void Operation3() {
		pState->Operation3();
		pState = pState->pNext;
	}
};

备忘录模式

模式定义

在不破坏封装性的前提下,捕获、企对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。

备忘录模式的使用场景

● 需要保存和恢复数据的相关状态场景。

● 提供一个可回滚(rollback) 的操作; 比如Word中的CTRL+Z组合键, IE浏览器中的后

退按钮, 文件管理器上的backspace键等。

● 需要监控的副本场景中。 例如要监控一个对象的属性, 但是监控又不应该作为系统的

主业务来调用, 它只是边缘应用, 即使出现监控不准、 错误报警也影响不大, 因此一般的做法是备份一个主线程中的对象, 然后由分析程序来分析。

● 数据库连接的事务管理就是用的备忘录模式, 想想看, 如果你要实现一个JDBC驱

动, 你怎么来实现事务? 还不是用备忘录模式嘛

class Memento
{
	string state;
public:
	Memento(const string & s) : state(s) {}
	string getState() const { return state; }
	void setState(const string & s) { state = s; }
};

class Originator
{
	string state;
public:
	Originator() {}
	Memento createMomento() {
		Memento m(state);
		return m;
	}
	void setMomento(const Memento & m) {
		state = m.getState();
	}
};

int main()
{
	Originator orginator;
	Memento mem = orginator.createMomento();
	//从备忘录中恢复
	orginator.setMomento(mem);
}

解释器模式

模式定义

给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。

解释器模式的优点

解释器是一个简单语法分析工具, 它最显著的优点就是扩展性, 修改语法规则只要修改相应的非终结符表达式就可以了, 若扩展语法, 则只要增加非终结符类就可以了。

解释器模式的缺点

● 解释器模式会引起类膨胀

● 解释器模式采用递归调用方法

● 效率问题

解释器模式的使用场景

● 重复发生的问题可以使用解释器模式

● 一个简单语法需要解释的场景

数据结构模式组

迭代器模式

迭代器模式(Iterator Pattern) 目前已经是一个没落的模式, 基本上没人会单独写一个迭代器, 除非是产品性质的开发, 其定义如下:

它提供一种方法访问一个容器对象中各个元素, 而又不需暴露该对象的内部细节。

组合模式

将对象组合成树形结构以表示“部分整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)。

class Component
{
public:
	virtual void process() = 0;
	virtual ~Component() {}
};
//树节点
class Composite : public Component {
	string name;
	list elements;
public:
	Composite(const string & s) : name(s) {}
	void add(Component* element) {
		elements.push_back(element);
	}
	void remove(Component* element) {
		elements.remove(element);
	}
	void process() {
		for (auto &e : elements)
			e->process(); //多态调用
	}
};
class Leaf : public Component {
	string name;
public:
	Leaf(string s) : name(s) {}
	void process() {
	}
};
void Invoke(Component & c) {
	c.process();
}
int main()
{
	Composite root("root");
	Composite treeNode1("treeNode1");
	Composite treeNode2("treeNode2");
	Leaf leaf1("left1");
	Leaf leaf2("left2");

	root.add(&treeNode1);
	treeNode1.add(&treeNode2);
	treeNode1.add(&leaf1);
	root.add(&treeNode2);
	Invoke(root);
}

责任链模式

模式定义

使多个对象都有机会处理逋求,丛而避免境求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

#include 
#include 
using namespace std;
enum class RequestType
{
	REQ_HANDLER1,
	REQ_HANDLER2,
	REQ_HANDLER3
};

class Reqest
{
	string description;
	RequestType reqType;
public:
	Reqest(const string & desc, RequestType type) : description(desc), reqType(type) {}
	RequestType getReqType() const { return reqType; }
	const string& getDescription() const { return description; }
};

class ChainHandler {
	ChainHandler *nextChain;
	void sendReqestToNextHandler(const Reqest & req)
	{
		if (nextChain != nullptr)
			nextChain->handle(req);
	}
protected:
	virtual bool canHandleRequest(const Reqest & req) = 0;
	virtual void processRequest(const Reqest & req) = 0;
public:
	ChainHandler() { nextChain = nullptr; }
	void setNextChain(ChainHandler *next) { nextChain = next; }
	void handle(const Reqest & req)
	{
		if (canHandleRequest(req))
			processRequest(req);
		else
			sendReqestToNextHandler(req);
	}
};

class Handler1 : public ChainHandler {
protected:
	bool canHandleRequest(const Reqest & req) override
	{
		return req.getReqType() == RequestType::REQ_HANDLER1;
	}
	void processRequest(const Reqest & req) override
	{
		cout << "Handler1 is handle reqest: " << req.getDescription() << endl;
	}
};

class Handler2 : public ChainHandler {
protected:
	bool canHandleRequest(const Reqest & req) override
	{
		return req.getReqType() == RequestType::REQ_HANDLER2;
	}
	void processRequest(const Reqest & req) override
	{
		cout << "Handler2 is handle reqest: " << req.getDescription() << endl;
	}
};
class Handler3: public ChainHandler {
protected:
	bool canHandleRequest(const Reqest & req) override
	{
		return req.getReqType() == RequestType::REQ_HANDLER2;
	}
	void processRequest(const Reqest & req) override
	{
		cout << "Handler2 is handle reqest: " << req.getDescription() << endl;
	}
};
int main() {
	Handler1 h1;
	Handler2 h2;
	Handler3 h3;
	h1.setNextChain(&h2);
	h2.setNextChain(&h3);
	Reqest req("process task ... ", RequestType::REQ_HANDLER3);
	h1.handle(req);
	return 0;
}

你可能感兴趣的:(C/C++)