C++设计模式

学习视频参考1

学习视频参考2

C++文档

我的Gitee源码

创建型模式

1 简单工厂

1.0 接口

接口:特殊的抽象类,一个纯虚的类,封装变化。
接口不变,内部的变化也不会影响到外部应用。
方便系统的维护和扩展。

首先是有一个api(类);
其次是继承这个api(类)实现接口内容;
最后是client调用这个(继承api类)的接口;
#include<iostream>
#include<string>
using namespace std;

class api
{
public:
	virtual void text(string s) = 0;
protected:
	api() {};				//屏蔽构造函数,实现接口概念
};

class impl:public api		//继承api(共有继承)
{
public:
	void text(string s)		//接口实现
	{
		cout << s << endl;
	}
};

int main()
{
	api* a = new impl();
	a->text("helloworld");	//调用接口
	delete a;
	return 0;
}

1.1结果

1.1 定义

面向接口编程
将上述实现接口的impl给隐藏起来
客户端只知道api类和一个factory类
其中factory是用来整合接口的(在factory创建impl类)

1.2 结构

1. 一个api类
2. 一群继承了api类的impl实现接口
3. 一个工厂类,用来隐藏new出来的impl类,同时返回地址。
4. client通过工厂来调用功能

1.3 实现

#include<iostream>
#include<string>
using namespace std;

class api 
{
public:
	virtual void text(string s) = 0;
protected:
	api() {};
};

class implone :public api
{
public:
	void text(string s)
	{
		cout << "one" << s << endl;
	}
};

class impltwo :public api
{
public:
	void text(string s)
	{
		cout <<"two" << s << endl;
	}
};

class factory
{
public:
	static api* createapi(int type)
	{
		api* aapi = nullptr;
		switch (type)
		{
		case 1:
		{
			aapi = new implone();
			break;
		}
		case 2:
		{
			aapi = new impltwo();
			break;
		}
		default:
		{
			break;
		}
		}
		return aapi;
	}
};

int main()
{
	api* anewapi = nullptr;
	anewapi = factory::createapi(1);
	anewapi->text("one");
	delete anewapi;
	anewapi = nullptr;
	anewapi = factory::createapi(2);
	anewapi->text("two");
	delete anewapi;
	anewapi = nullptr;
	return 0;
}

结果2

可以说实现了client对implone和impltwo的解耦合
也就是client只调用了factory而没有调用implone和impltwo
factory实现了变化隔离

1.4 C++对象动态创建(完全解耦)

实现对implone和impltwo动态创建(即是对implone和impltwo两个类的功能和类名的隐藏)
#include<string>
#include<map>
#include<iostream>

typedef void* (*Constructor)();	//函数指针

//帮助动态创建
class COBject
{
public:
	//存放类名和其对应的构建函数
	static void registerClass(std::string classname,Constructor constructor)
	{
		constructors()[classname] = constructor;
	}

	//取出按类名取出对应的构函数
	static void* createOBject(const std::string& classname)
	{
		Constructor constructor = nullptr;
		//如果找到了对应的构建函数
		if (constructors().find(classname) != constructors().end())
		{
			constructor = constructors().find(classname)->second;//first是key,second是构建函数
		}
		if (constructor == nullptr)
		{
			return nullptr;
		}
		return (*constructor)();
	}

private:
	inline static std::map<std::string, Constructor>& constructors()
	{
		//map<类名,构建的函数>
		static std::map<std::string, Constructor> instance;
		return instance;
	}

};


#define REG_CLASS(classname)												\
class classname##help														\
{																			\
public:																		\
	impltwohelp()															\
	{																		\
		COBject::registerClass(#classname, classname##help::createOBjfunc);	\
	}																		\
	static void* createOBjfunc()											\
	{																		\
		return new classname;												\
	}																		\
};																			\
classname##help classname##help;

class api
{
public:
	virtual void text(std::string s) = 0;
protected:
	api() {};

};

class implone :public api
{
public:
	void text(std::string s)
	{
		std::cout << "one" << s << std::endl;
	}
};

class impltwo :public api
{
public:
	void text(std::string s)
	{
		std::cout << "two" << s << std::endl;
	}
};

class autofactory
{
public:
	static api* createapi()
	{
		api* apip = nullptr;
		//从配置文件中读入类名
		REG_CLASS(impltwo);
		apip = static_cast<api*>(COBject::createOBject("impltwo"));
		return apip;
	}
};

int main()
{
	api* apip = autofactory::createapi();
	apip->text("success");
	return 0;
}
视频是有几个问题的,
1. 是在注册宏REG_CLASS(classname)当中的createOBjfunc()函数下视频是直接给他返回impltwo,我这是直接改成了classname。
2. 配置化开发,通过配置读入字符串,然后自动生成对象才能完成自动化创建,
   这里的问题是你要生成一个自动化生成一个对象,如implone,你要从配置文件读入字符串"implone",
   然后修改REG_CLASS(impltwo)COBject::createOBject("impltwo")中的impltwo为implone,
   涉及到字符串转为非字符串的问题,因为你也不能让使用者知道implone和impltwo的类名。
   但如果用户可以知道类名的话,可以把REG_CLASS(classname)的放到你要用之前声明。
大家也要积极思考

思想主要是
利用map<"类名",对应类的创建函数>来对类进行管理
优点
1. 配置化开发
2. 完全解耦合
如果你建立了一个新的类,想要测试这个新类的话,自需要改配置文件就行了 
而不用修改之前的东西方便维护和开发

2 工厂模式

2.1 定义

对简单工厂更进一步的抽象和阐释,可以说是简单工厂的经验总结

1. 功能: 
	父类不知道具体实现的情况下,完成自身功能的调用,而具体的实现延迟到子类来实现。
2. 实现成抽象类
	通常父类是一个抽象类,里面包含所创建对象的抽象方法,这些抽象方法就是工厂方法。
3. 实现成具体类
	可以把父类实现为一个具体的类,父类提供所需对象的默认实现方法。
	这样做的话,没有具体的子类也可以运行。
4. 工厂方法的参数和返回值
	工厂方法实现中,可能需要参数来选择哪一种具体的实现。
	一般工厂方法放回的是被创建对象的接口对象,也可以是抽象类或一个具体类的实例。

2.2 结构

1. 一个api类
2. 一群继承了api类的impl实现接口
3. 一个工厂类,包含工厂方法
4. 一群继承了工厂类的方法对象(调用或实现接口impl)
5. client通过调用工厂方法来调用功能
这样客户端就只知道3和4而不知道1和2。

2.3 场景

例如: 一个导出数据的应用框架
客户选择导出方式并真正导出数据
客户只知道接口而不知道导出的实现内容。

2.4 实现

#include <iostream>
#include <string>
using namespace std;

class export_file_api
{
public:
	virtual bool exportdata(string data) = 0;
protected:
	export_file_api() {};
};

//两个具体子类
class export_txt_file : public export_file_api
{
	bool exportdata(string data)
	{
		cout << "导出txt:" << data << endl;
		return true;
	}
};

class export_csv_file : public export_file_api
{
	bool exportdata(string data)
	{
		cout << "导出csv:" << data << endl;
		return true;
	}
};

//实现一个export_operation,导出数据的业务功能
class export_operation
{
public:
	bool export_data(string data)
	{
		export_file_api* pApi = factoryMethod();
		return pApi->exportdata(data);
	}

protected:
	virtual export_file_api* factoryMethod() = 0;
};


//实现工厂方法factoryMethod()
class exporttxtoperation : public export_operation
{
protected:
	export_file_api* factoryMethod()
	{
		return new export_txt_file;
	}

};

class exportcsvoperation : public export_operation
{
protected:
	export_file_api* factoryMethod()
	{
		return new export_csv_file;
	}
};


int main()
{
	export_operation* pOperation;
	pOperation = new exporttxtoperation;
	pOperation->export_data("txt");
	delete pOperation;
	pOperation = new exportcsvoperation;
	pOperation->export_data("csv");
	delete pOperation;
	return 0;
}

结果4

客户端只需知道工厂类和工厂方法就可以完成功能调用而不用关心具体的实现
方便扩展和维护。
主要还是为了 解耦合

2.5 IOC容器和工厂模式的应用

依赖注入:
	应用程序依赖容器创建并注入它所需的外部资源。
控制反转:
	容器控制应用程序,由容器反向的向应用程序注入应用程序所需的东西。

例子
对象A向IOC容器申请调用对象B
IOC容器看对象B是否准备好了
	准备好了就注入对象A

这样做的好处就是IOC容器在控制应用程序
你的应用程序就不在是由某个主体支配的,而是由某个框架来支配的
耦合度进一步降低,软件架构更加灵活,有利于功能复用
对象多的时候就很好用。
我今年大三的数据库课设就是对象太多搞死我了。
//IOC容器
template <class T>
class IOCcontainer
{
public:
	IOCcontainer() {};
	~IOCcontainer() 
	{
	};

	//注册需要创建对象的构造函数,通过唯一的标识符,以便于以后查找
	template <class driver>
	void registerType(string str_key)
	{
		std::function < T* ()> function = []
		{
			return new driver;
		};
		registerType(str_key, function);
	}

	//根据唯一的标识去查找构造函数
	T* resolve(string str_key)
	{
		//没有这个构造函数
		if (m_createMap.find(str_key) == m_createMap.end())
		{
			return nullptr;
		}
		//有这个构造函数
		std::function<T*()> function = m_createMap[str_key];
		return function();
	}

	//创建智能指针 为了能快速访问对象
	std::shared_ptr<T> resilveshare(string str_key)
	{
		T* ptr = resolve(str_key);
		return std::shared_ptr<T>(ptr);
	}

private:
	void registerType(string str_key, std::function<T* ()> creator)
	{
		//如果存在的话
		if (m_createMap.find(str_key) != m_createMap.end())
		{
			throw std::invalid_argument("已经存在这个key了");
		}
		m_createMap.emplace(str_key, creator);
	}
private:
	map<string, std::function<T* ()>> m_createMap;
};
//演示对象
class ICar
{
public:
	ICar(){};
	virtual void test()  = 0;
};

class Bus :public ICar
{
public:
	void test() { cout << "BusBus" << endl; };
};

class Track :public ICar
{
public:
	void test() { cout << "TrackTrack" << endl; };
};
int main()
{
	IOCcontainer<ICar> carIoc;
	carIoc.registerType<Bus>("1");
	carIoc.registerType<Track>("2");

	std::shared_ptr<ICar> Bus = carIoc.resilveshare("1");
	Bus->test();
	std::shared_ptr<ICar> Track = carIoc.resilveshare("2");
	Track->test();
	return 0;
}

结果5

2.6 总结

工厂模式的本质:
	1. 依赖倒置原则
		要依赖抽象,而不是某个具体的类,
		不能使高层组件依赖与低层组件,高层和低层组件都应该依赖与抽象。
	2. 让子类选择实现
		子类实现抽象类的方法

何时选用工厂模式的方法:
1. 如果一个类需要创建某个接口的对象,但又不知道具体的实现,可以选择工厂模式,
	把创建对象的工作延迟到子类去实现。
2. 如果一个类本身就希望子类来创建所需对象的时候,应该使用工厂模式。

3 抽象工厂模式

3.1 场景

考虑一个实际应用 :我们需要两套gui表示层,一个在pc机,另一个在平板,手机上完成应用程序界面
从封装的角度来说,我们希望根据布局器配置我们的控件,我们开放布局器接口和控件接口,供给客户端使用
如果不用抽象工厂模式的话,我们可以使用简单工厂
一个布局api,一个pc实现布局api,一个moblie实现布局api,一个工厂
传入1,pc.
传入2,moblie

3.2 定义

可以说是合并同类项,提取公因式。
把相同有关联的东西合并在一起。
1. 抽象工厂模式的功能
	为一系列相关的对象或相互依赖的对象创建一个接口
	从某种意义上看,抽象工厂其实是一个产品系列
2. 实现成接口
	AbstractFactory实现成为接口
3. 使用工厂方法
	AbstractFactory定义了创建产品所需的接口,具体的实现在实现类里面,
	通常在实现类里面就需要选择更多具体的实现,
	所以AbstractFactory定义的创建产品的方法可以看作是工厂方法,
	工厂方法的具体实现也就延迟到了具体的工厂里面。
	也就是使用工厂方法实现抽象方法
4. 切换产品簇
	抽象工厂定义了一个产品簇,因此切换产品簇的时候切换不同的抽象工厂就行

3.3 结构

1. 各种api类
2. 各种api实现类
3. abstractFactory接口
4. abstractFactory实现类(组合各种api类)
5. 调用abstractFactory接口

3.4 实现

//frameApi
class frameApi
{
public:
	virtual void draw() = 0;

protected:
	frameApi() {};
};

//PC分辨率
class PCFrame : public frameApi
{
public:
	PCFrame(int pins) :my_pins(pins)
	{

	};
	void draw()
	{
		cout << "PC分辨率" << my_pins << endl;
	}
private:
	int my_pins;		//分辨率数值
};

//moblie分辨率
class moblie : public frameApi
{
public:
	moblie(int pins) :my_pins(pins)
	{

	};
	void draw()
	{
		cout << "moblie分辨率" << my_pins << endl;
	}
private:
	int my_pins;		//分辨率数值
};
//布局api
class layoutApi
{
public:
	virtual void installframe() = 0;
protected:
	layoutApi() {};
};


//高分辨率布局
class highlayout : public layoutApi
{
public:
	highlayout(int pins) : my_frameAdpaterPins(pins)
	{

	};
	void installframe()
	{
		cout << "PC下,高分辨率布局" << my_frameAdpaterPins << endl;
	};

private:
	int my_frameAdpaterPins;
};

//低分辨率布局
class lowlayout : public layoutApi
{
public:
	lowlayout(int pins) : my_frameAdpaterPins(pins)
	{

	};
	void installframe()
	{
		cout << "moblie下,低分辨率布局" << my_frameAdpaterPins << endl;
	};

private:
	int my_frameAdpaterPins;
};

//抽象工厂 用来定义产品簇,有两个模式
class AbstractFactory
{
public:
	virtual frameApi* createframeApi() = 0;
	virtual layoutApi* createlayoutApi() = 0;
protected:
	AbstractFactory()
	{

	};
};

class Schemal : public AbstractFactory
{
public:
	frameApi* createframeApi()
	{
		return new PCFrame(1024);
	};
	layoutApi* createlayoutApi()
	{
		return new highlayout(1024);
	};
};

class Schema2 : public AbstractFactory
{
public:
	frameApi* createframeApi()
	{
		return new moblie(800);
	};
	layoutApi* createlayoutApi()
	{
		return new lowlayout(800);
	};
};

//gui引擎,用来调用模式的
class guiengineer
{
public:
	void prepare_Materials(AbstractFactory * pSchema)
	{
		this->pframeApi = pSchema->createframeApi();
		this->playoutApi = pSchema->createlayoutApi();
		pframeApi->draw();
		playoutApi->installframe();
	}


private:
	frameApi* pframeApi;
	layoutApi* playoutApi;
};
int main()
{
	guiengineer* p = new guiengineer();
	p->prepare_Materials(new Schemal());
	return 0;
}

结果6

这样就不需要创建多个工厂

3.5 抽象工厂的使用场景

图片1

但实际上我个人才只接触了qt
qt的控件差不多继承了一个叫QObject的基类
那么应该也适合使用抽象工厂,甚至本身用的是抽象工厂模式(以后有机会我就去看看源码)

何时选用抽象工厂

1. 如果希望一个系统独立于它的产品创建,组合和表示的时候,换句话说,希望一个系统只是知道产品的接口而不关心实现的时候
2. 如果一个系统要由多个产品系列中的一个来配置的时候,换句话说,就是可以动态的切换产品簇的时候
3. 如果要强调一系列相关产品的接口,以便联合使用它们的时候 

3.6 优缺点

优点:
	1. 分离接口和实现
	2. 切换产品簇变得容易
缺点
	1. 不太容易扩展新的产品
	2. 容易造成类层次复杂

4 单例模式

在全局中有且只有一个类

4.1 懒汉模式

class sigle
{
public:
	static sigle* getinstance()
	{
		if (instance == nullptr)
		{
			instance = new sigle;
		}
		return instance;
	}
private:
	static sigle* instance;

};

sigle* sigle::instance = nullptr;

int main()
{
	sigle* asigle = sigle::getinstance();
	delete asigle;
	return 0;
}
1. 在外部声明instance = nullptr;
2. 延迟加载,不用就不创建,节省空间
3. 调用getinstance方法
4. 因为static静态,所以是全局唯一的。

4.2 饿汉模式

class sigle
{
public:
	static sigle* getinstance()
	{
		if (instance == nullptr)
		{
			instance = new sigle;
		}
		return instance;
	}
private:
	static sigle* instance;

};

sigle* sigle::instance = new sigle;

int main()
{
	sigle* asigle = sigle::getinstance();
	delete asigle;
	return 0;
}
1. 在外部声明instance = new sigle;
2. 直接就声明了类,比较占用空间
3. 调用getinstance方法
4. 因为static静态,所以是全局唯一的。

4.3 多线程安全

以上可以看出,懒汉和饿汉的区别在于是否在外部new了类
而C++中构造函数不在线程安全,懒汉模式是线程不安全的
if (instance == nullptr)
{
	instance = new sigle;
}
return instance;
想想看上述代码在两个线程中执行
线程a准备new一个sigle(还没new,此时instance = nullptr)
线程b此时开始判断了instance为nullptr,下一步就是要new sigle
那么此时,线程a和线程b都要new一遍sigle。
这是懒汉模式线程安全中的问题。
要解决懒汉模式线程安全中的问题可以加而互斥量。
当线程执行上述代码的时候上锁,其他线程就只能等待,从而避免了重复创建对象的问题
而饿汉模式是先在外部new了sigle,从而避免了上述问题。

4.4 缓存和单例->多例

4.4.1 缓存和单例

缓存利用一个全局的map来实现
#include <iostream>
#include <map>
using namespace std;

class sigle;
static map<string, sigle*> mymap = map<string, sigle*>();	//缓存位置

class sigle
{
public:
	static sigle* getinstance()
	{
		if (mymap.find(default_key) != mymap.end())
		{
			return mymap.find(default_key)->second;			//从缓存中返回指针
		}
		if (instance == nullptr)
		{
			instance = new sigle;
			mymap[default_key] = instance;					//缓存记录指针
		}
		return instance;
	}
private:
	static sigle* instance;
	static string default_key;
};

string sigle::default_key = "one";
sigle* sigle::instance = nullptr;


int main()
{
	sigle* p1 = sigle::getinstance();
	sigle* p2 = sigle::getinstance();
	cout << p1 << endl;
	cout << p2 << endl;
	return 0;
}

4.4.2 多例

从缓存中不断的循环读取实例
#include <iostream>
#include <map>

using namespace std;

//设置缓存
const static int NUM_MAX = 5;						//实例最大数
class sigle;
static map<int, sigle*> mymap = map<int, sigle*>();	//缓存

class sigle
{
public:
	static sigle* getinstance()
	{
		instance = mymap[m_instance_count];

		if (instance == nullptr)
		{
			instance = new sigle;
			mymap[m_instance_count] = instance;
		}
		//类似于一个循环,每次调用的话就使得m_instance_count++,后面就调用下一个
		m_instance_count++;
		if (m_instance_count >= NUM_MAX)
		{
			m_instance_count = 0;
		}

		return instance;
	}
private:
	sigle()
	{
		cout << "building" << endl;
	}

private:
	static sigle* instance;
	static int m_instance_count;		//存放实例个数

};
sigle* sigle::instance = nullptr;
int sigle::m_instance_count = 0;


int main()
{
	for (int i = 0; i < 5; i++)
	{
		sigle* p = sigle::getinstance();
	}
	for (int i = 0; i < 10; i++)
	{
		sigle* p = sigle::getinstance();
		cout << p << " ";
		if (i == 4)
		{
			cout << endl;
		}
	}
	return 0;
}

结果3

可以看出总共创建了5个实例
后面再次调用的话会循环从map中读取已经创建好的实例

5 Bulider模式

5.1 场景

银行对账单的导出数据的应用
1. 导出的文件包括文件头,文件体,文件尾
2. 文件头部分: 分公司编号,导出数据日期,等等
3. 文件体部分: 表名称,分条描述数据
4. 文件尾部分: 输出人

5.2 定义

	功能
		构建复杂产品,细化,分步骤的构建产品,也解决就是一步一步构造复杂对象的问题
		这个构建的过程是统一的,固定不变的,变化的部分放到生成器部分。
		只要配置不同的生成器,那么同样的构建过程就能构建出不同的产品表示出来
		
		分离构建算法和具体的构造实现,使得构建算法可以重用。
		具体的构建算法可以很方便的扩展和切换,从而可以灵活的组合来构造出不同的产品对象。

5.3 结构

1. Bulider接口: 定义了如何构建各个部件。
2. director: 如何组合来构建产品,
总的来说,就是一部分是部件构造和产品装配(Bulider),另一部分是整体构建算法(director).。
在Bulider模式中,强调的是固定整体的构建算法,而灵活扩展和切换部件的具体构造和产品装配的方式

5.4 实现

//文件头
class ExportHeaderModel
{
public:
	ExportHeaderModel(string strDeptid, string strExportdata)
		:m_strDepId(strDeptid), m_strExportDate(strExportdata)
	{

	};
	string getStrDepid()
	{
		return m_strDepId;
	}
	string getExportdate()
	{
		return m_strExportDate;
	}
private:
	string m_strDepId;		//对账单的部门id
	string m_strExportDate;	//对账单的导出日期
};

//文件体
class ExportDataModel
{
public:
	ExportDataModel(string strTransId, double Quantity)
		: m_strTransId(strTransId), m_Quantity(Quantity)
	{

	};
	string getstrTransId()
	{
		return m_strTransId;
	};
	double getQuantity()
	{
		return m_Quantity;
	}

private:
	string m_strTransId;	//交易id
	double m_Quantity;
};

//文件尾
class ExportFooterModel
{
public:
	ExportFooterModel(string exportUser)
		: m_exportUser(exportUser)
	{

	};
	string getexportUser()
	{
		return m_exportUser;
	};

private:
	string m_exportUser;	//导出人
};
如果按照一般解决思路,就是写一个输出类,包括对文件头,体,尾的写入,
这时候就有问题了,
你要输出为txt,那么就要写一个输出txt类,
你要输出为xml,你就要写一个输出xml类,
而这些类的算法实现是差不多的,就是写入头,体尾。
这时侯就可以使用构建者模式
class Bulider
{
public:
	virtual void bulidHeader(ExportHeaderModel& ehm) = 0;				//处理文件头
	virtual void bulidData(vector<ExportDataModel*>& edmcollection) = 0;//处理文件体
	virtual void bulidFooter(ExportFooterModel& efm) = 0;				//处理文件尾
	virtual string result() = 0;										//输出
protected:
	Bulider() {};
};


class TxtBulider : public Bulider
{
public:
	void bulidHeader(ExportHeaderModel& ehm)
	{
		m_strResult = m_strResult + ehm.getStrDepid() + "," + ehm.getExportdate() + "\n";
	}
	void bulidData(vector<ExportDataModel*>& edmcollection)
	{
		for (vector< ExportDataModel*>::iterator iter = edmcollection.begin();
			iter != edmcollection.end(); iter++)
		{
			m_strResult = m_strResult + (*iter)->getstrTransId() + ":" + ConvertToString((*iter)->getQuantity()) + "\n";
		}
	}
	void bulidFooter(ExportFooterModel& efm)
	{
		m_strResult = m_strResult + efm.getexportUser() + "\n";
	}
	string result()
	{
		return m_strResult;
	}
private:
	string m_strResult;
};

class XmlBulider : public Bulider
{
public:
	void bulidHeader(ExportHeaderModel& ehm)
	{
		//使用字符串拼接
		strTemp.append("<?xml.version='1.0' encoding='utf-8'>\n");
		strTemp.append("<Receipt>\n");
		//写文件头
		strTemp.append("	<Header>\n");
		strTemp.append("		<DepId>" + ehm.getStrDepid() + "</DepId>\n");
		strTemp.append("		<ExportDate>" + ehm.getExportdate() + "</ExportDate>\n");
		strTemp.append("	</Header>\n");
	}
	void bulidData(vector<ExportDataModel*>& edmcollection)
	{
		//写文件体
		strTemp.append("	<Body>\n");
		for (vector< ExportDataModel*>::iterator iter = edmcollection.begin();
			iter != edmcollection.end(); iter++)
		{
			strTemp.append("		<id>" + (*iter)->getstrTransId() + "</id>\n");
			strTemp.append("		<amount>" + ConvertToString((*iter)->getQuantity()) + "</amount>\n");
		}
		strTemp.append("	</Body>\n");
	}
	void bulidFooter(ExportFooterModel& efm)
	{
		//写文件尾
		strTemp.append("	<Footer>\n");
		strTemp.append("		<ExportUser>" + efm.getexportUser() + "</ExportUser>\n");
		strTemp.append("	</Footer>\n");
	}
	string result()
	{
		return strTemp;
	}
private:
	string strTemp ;
};

class Director
{
public:
	Director(Bulider* p) : p(p)
	{

	};
	void construct(ExportHeaderModel& ehm, vector<ExportDataModel*>& edmcollection, ExportFooterModel& efm)
	{
		p->bulidHeader(ehm);
		p->bulidData(edmcollection);
		p->bulidFooter(efm);
	}
private:
	Bulider* p;
};
int main()
{
	ExportHeaderModel* pEhm = new ExportHeaderModel("公司id", "2023-1-15");

	ExportDataModel* pEdm1 = new ExportDataModel("1", 10000.00f);
	ExportDataModel* pEdm2 = new ExportDataModel("2", 20000.00f);
	vector<ExportDataModel*> myVec;
	myVec.push_back(pEdm1);
	myVec.push_back(pEdm2);

	ExportFooterModel* pEfm = new ExportFooterModel("张");

	Bulider* p;
	Director* dp;
	p = new TxtBulider;
	dp = new Director(p);
	dp->construct(*pEhm, myVec, *pEfm);
	cout << p->result() << endl;
	delete dp;
	delete p;
	
	p = new XmlBulider;
	dp = new Director(p);
	dp->construct(*pEhm, myVec, *pEfm);
	cout << p->result() << endl;
	delete dp;
	delete p;
	return 0;
}

结果7

6 原型模式

6.1 场景

图片3

6.2 定义

功能
通过克隆来创建一个新的对象实例。
(在裂变对象的时候很有价值)

6.3 结构

在抽象类中加一个克隆接口。
在实现类中实现这个克隆接口。

6.4 实现

template <class T>
string iToStr(T value)
{
	stringstream ss;
	ss << value;
	return ss.str();
};

class OrderApi
{
public:
	virtual void setOrderProductNum(int num) = 0;
	virtual int getOrderProductNum() = 0;

	virtual void setCustomerName(string strCustomerName) = 0;
	virtual string getCustomerName() = 0;

	virtual void setProductId(string strproductId) = 0;
	virtual string getProductId() = 0;
	
	virtual string getOrderContent() = 0;

protected:
	OrderApi() {};
};

class HomeOrder : public OrderApi
{
public:
	void setOrderProductNum(int num)
	{
		m_orderProductNum = num;
	};
	int getOrderProductNum()
	{
		return m_orderProductNum;
	};

	void setCustomerName(string strCustomerName)
	{
		m_strCustomerName = strCustomerName;
	};
	string getCustomerName()
	{
		return m_strCustomerName;
	};

	void setProductId(string strproductId)
	{
		m_strProductId = strproductId;
	};
	string getProductId()
	{
		return m_strProductId;
	}

	string getOrderContent()
	{
		return "客户名称" + m_strCustomerName +
			"数量" + iToStr(m_orderProductNum) +
			"订单id" + m_strProductId;
	};

private:
	string m_strCustomerName;	//名称
	string m_strProductId;		//id
	int m_orderProductNum;		//数量
};

class AboardOrder : public OrderApi
{
public:
	void setOrderProductNum(int num)
	{
		m_orderProductNum = num;
	};
	int getOrderProductNum()
	{
		return m_orderProductNum;
	};

	void setCustomerName(string strCustomerName)
	{
		m_strCustomerName = strCustomerName;
	};
	string getCustomerName()
	{
		return m_strCustomerName;
	};

	void setProductId(string strproductId)
	{
		m_strProductId = strproductId;
	};
	string getProductId()
	{
		return m_strProductId;
	}

	string getOrderContent()
	{
		return "客户名称" + m_strCustomerName +
			"数量" + iToStr(m_orderProductNum) +
			"订单id" + m_strProductId;
	};

private:
	string m_strCustomerName;	//名称
	string m_strProductId;		//id
	int m_orderProductNum;		//数量
};

下面是我们的一般解决思路
class OrderBusiness
{
public:
	void saveOrder(OrderApi* pOrder)
	{
		OrderApi* pNewOrder = nullptr;
		//判读一下,工作数量有无超过200
		while (pOrder->getOrderProductNum() > 200)
		{
			//新建一个订单,不知道是homeorder还是abroadorder
			//所以可以用类型判断
			//代码的坏味道,这时候要进行重构
			if (dynamic_cast<HomeOrder*>(pOrder) != nullptr)
			{
				//创建一个新对象,去暂存目标
				HomeOrder* p2 = new HomeOrder;
				HomeOrder* p1 = static_cast<HomeOrder*>(pOrder);
				p2->setOrderProductNum(200);
				p2->setCustomerName(p1->getCustomerName());
				p2->setProductId(p1->getProductId());
				pNewOrder = p2;
			}
			if (dynamic_cast<AboardOrder*>(pOrder) != nullptr)
			{
				//创建一个新对象,去暂存目标
				AboardOrder* p2 = new AboardOrder;
				AboardOrder* p1 = static_cast<AboardOrder*>(pOrder);
				p2->setOrderProductNum(200);
				p2->setCustomerName(p1->getCustomerName());
				p2->setProductId(p1->getProductId());
				pNewOrder = p2;
			}
			//原来的订单保留,数量要减少200
			pOrder->setOrderProductNum(pOrder->getOrderProductNum() - 200);
			cout << "新订单" << pNewOrder->getOrderContent() << endl;
		}
		//不超过两百个
		cout << "最终订单是" << pOrder->getOrderContent() << endl;
	}
};

int main()
{
	HomeOrder* pHome = new HomeOrder;
	pHome->setOrderProductNum(512);
	pHome->setCustomerName("haha");
	pHome->setProductId("jasdljflk");

	OrderBusiness* pOb = new OrderBusiness();
	pOb->saveOrder(pHome);

	return 0;
}

结果8

这里可以注意到,两个if语句里面的内容是几乎是相同的,这时候要进行重构
不由得想到我上学期数据库课程设计里面的QTcpsocket收发协议包用的switch(类型),满满的坏味道。

重构

出现问题原因在于我们的OrderApi类没有识别能力
就是不知道是homeorder还是abroadorder。
问题中我们可以在抽象类在加一个virtual OrderApi* cloneOrder() = 0;接口
用来返回自己。
template <class T>
string iToStr(T value)
{
	stringstream ss;
	ss << value;
	return ss.str();
};

class OrderApi
{
public:
	virtual void setOrderProductNum(int num) = 0;
	virtual int getOrderProductNum() = 0;

	virtual void setCustomerName(string strCustomerName) = 0;
	virtual string getCustomerName() = 0;

	virtual void setProductId(string strproductId) = 0;
	virtual string getProductId() = 0;
	
	virtual string getOrderContent() = 0;
	virtual OrderApi* cloneOrder() = 0;

protected:
	OrderApi() {};
};

class HomeOrder : public OrderApi
{
public:
	void setOrderProductNum(int num)
	{
		m_orderProductNum = num;
	};
	int getOrderProductNum()
	{
		return m_orderProductNum;
	};

	void setCustomerName(string strCustomerName)
	{
		m_strCustomerName = strCustomerName;
	};
	string getCustomerName()
	{
		return m_strCustomerName;
	};

	void setProductId(string strproductId)
	{
		m_strProductId = strproductId;
	};
	string getProductId()
	{
		return m_strProductId;
	}

	string getOrderContent()
	{
		return "客户名称" + m_strCustomerName +
			"数量" + iToStr(m_orderProductNum) +
			"订单id" + m_strProductId;
	};
	
	//重构----------------
	OrderApi* cloneOrder()
	{
		HomeOrder* pHomeOrder = new HomeOrder;
		pHomeOrder->setCustomerName(m_strCustomerName);
		pHomeOrder->setProductId(m_strProductId);
		pHomeOrder->setOrderProductNum(m_orderProductNum);
		return pHomeOrder;
	};
private:
	string m_strCustomerName;	//名称
	string m_strProductId;		//id
	int m_orderProductNum;		//数量
};

class AboardOrder : public OrderApi
{
public:
	void setOrderProductNum(int num)
	{
		m_orderProductNum = num;
	};
	int getOrderProductNum()
	{
		return m_orderProductNum;
	};

	void setCustomerName(string strCustomerName)
	{
		m_strCustomerName = strCustomerName;
	};
	string getCustomerName()
	{
		return m_strCustomerName;
	};

	void setProductId(string strproductId)
	{
		m_strProductId = strproductId;
	};
	string getProductId()
	{
		return m_strProductId;
	}

	string getOrderContent()
	{
		return "客户名称" + m_strCustomerName +
			"数量" + iToStr(m_orderProductNum) +
			"订单id" + m_strProductId;
	};
	
	//重构----------------
	OrderApi* cloneOrder()
	{
		AboardOrder* pHomeOrder = new AboardOrder;
		pHomeOrder->setCustomerName(m_strCustomerName);
		pHomeOrder->setProductId(m_strProductId);
		pHomeOrder->setOrderProductNum(m_orderProductNum);
		return pHomeOrder;
	};
private:
	string m_strCustomerName;	//名称
	string m_strProductId;		//id
	int m_orderProductNum;		//数量
};

class OrderBusiness
{
public:
	void saveOrder(OrderApi* pOrder)
	{
		OrderApi* pNewOrder = nullptr;
		//判读一下,工作数量有无超过200
		while (pOrder->getOrderProductNum() > 200)
		{
			//新建一个订单
			pNewOrder = pOrder->cloneOrder();
			pNewOrder->setOrderProductNum(200);
			//原来的订单保留,数量要减少200
			pOrder->setOrderProductNum(pOrder->getOrderProductNum() - 200);
			cout << "新订单" << pNewOrder->getOrderContent() << endl;
		}
		//不超过两百个
		cout << "最终订单是" << pOrder->getOrderContent() << endl;
	}
};


int main()
{
	HomeOrder* pHome = new HomeOrder;
	pHome->setOrderProductNum(512);
	pHome->setCustomerName("haha");
	pHome->setProductId("jasdljflk");

	OrderBusiness* pOb = new OrderBusiness();
	pOb->saveOrder(pHome);
	return 0;
}

结果9

以上是创建型模式,主要是创建对象的时候不直接去new


2023年1月26日

结构型模式

1 适配器模式

1.1 定义

将一个类的接口转化为客户希望的另外的一个接口
使得原来接口不兼容的两个类可以一起工作
1. 对象适配器
	适用与当个对象进行接口转化,十分灵活
2. 类的适配器
	继承类,专属与这个类的接口转化

1.2 结构

1. 对象适配器
	传入要适配的对象,在进行接口转化
2. 类的适配器
	多继承(要变的接口类,要变成的接口类),进行接口转化

1.3 实现

  1. 对象适配器
#include <iostream>
using namespace std;

class ThreePhaseOutlet
{
public:
	void doThreePhasePlug()
	{
		cout << "三相插头接入" << endl;
	}
};

class TwoPhaseOutlet
{
public:
	virtual void doPlug() = 0;
};

//对象适配器
class OutletObjConvertor :public TwoPhaseOutlet
{
public:
	OutletObjConvertor(ThreePhaseOutlet* pOut) :m_pOut(pOut)
	{

	}
	void doPlug()
	{
		doConvert();
		m_pOut->doThreePhasePlug();
	}
	void doConvert()
	{
		cout << "对一个二插头本身进行转化" << endl;
	}
private:
	ThreePhaseOutlet* m_pOut;
};

int main()
{
	TwoPhaseOutlet* pOutlet = new OutletObjConvertor(new ThreePhaseOutlet);
	pOutlet->doPlug();
}

对象适配器结果

  1. 类的适配器
#include <iostream>
using namespace std;

class ThreePhaseOutlet
{
public:
	void doThreePhasePlug()
	{
		cout << "三相插头接入" << endl;
	}
};

class TwoPhaseOutlet
{
public:
	virtual void doPlug() = 0;
};

//类适配器
class OutletConverter :public TwoPhaseOutlet, public ThreePhaseOutlet
{
public:
	void doPlug()
	{
		doConvertor();
		doThreePhasePlug();
	}
	void doConvertor()
	{
		cout << "二插头转为三插头" << endl;
	}
};

int main()
{
	TwoPhaseOutlet* pOutlet = new OutletConverter();
	pOutlet->doPlug();
}

类适配器结果

1.4 STL适配器

用STL默认的适配器,将deque转化为stack.
#include <iostream>
#include <deque>
#include <stack>

using namespace std;

int main()
{
	deque<int> m_deque(3, 100);
	stack<int> m_stack;
	stack<int> m_stack2(m_deque);
	cout<< "m_stack的size " << m_stack.size() << endl;
	cout << "m_stack2的size " << m_stack2.size() << endl;

	m_stack2.pop();

	cout << "m_stack2的size " << m_stack2.size() << endl;
	cout << "m_stack2的top " << m_stack2.top() << endl;

	return 0;
}

stl适配器结果1

2 门面模式

2.1 定义

提供一个统一的接口去访问多个子系统的多个不同接口
为子系统的一组接口提供了一个统一高层次的接口,使得子系统的接口更容易使用

门面模式定义

2.2 结构

结构也很简单,就是把多个接口封装进一个接口里

2.3 实现

一组接口

class Carmera
{
public:
	void turnOn()
	{
		cout << "开启相机" << endl;
	}
	void turnOff()
	{
		cout << "关闭相机" << endl;
	}
};

class Light
{
public:
	void turnOn()
	{
		cout << "开启照明灯" << endl;
	}
	void turnOff()
	{
		cout << "关闭照明灯" << endl;
	}
};

class Sensor
{
public:
	void active()
	{
		cout << "传感器启动" << endl;
	}
	void deactive()
	{
		cout << "传感器关闭" << endl;
	}
};

class Alarm
{
public:
	void active()
	{
		cout << "报警器启动" << endl;
	}
	void deactive()
	{
		cout << "报警器关闭" << endl;
	}
};

如果我们直接在客服端使用(main函数下使用)
那么我们要分别执行四个接口来启动
执行四个接口来关闭
对客户端来说,要知道的东西太多了
所以我们就把这些接口封装起来
class SecuritFacade
{
public:
	SecuritFacade()
	{
		m_Carmera = new Carmera;
		m_Light = new Light;
		m_Sensor = new Sensor;
		m_Alarm = new Alarm;
	}
	void active()
	{
		m_Carmera->turnOn();
		m_Light->turnOn();
		 m_Sensor->active();
		m_Alarm->active();
	}
	void deactive()
	{
		m_Carmera->turnOff();
		m_Light->turnOff();
		m_Sensor->deactive();
		m_Alarm->deactive();
	}
private:
	Carmera* m_Carmera;
	Light* m_Light;
	Sensor* m_Sensor;
	Alarm* m_Alarm;

};

int main()
{
	SecuritFacade* m_SecuritFacade = new SecuritFacade;
	m_SecuritFacade->active();
	m_SecuritFacade->deactive();
	return 0;
}

门面模式结果1

3 享元模式

3.1 场景

享元模式

3.2 定义

减少对象的数量,提高对象的复用率,节约系统资源。
利用共享技术实现对大量细粒度对象的复用

3.3 实现

下棋为例
 //棋子的颜色
enum PieceColor
{
	BLACK,
	WHITE
};

//棋子的位置
struct PiecePos
{
	int x;
	int y;
	PiecePos(int a,int b):x(a),y(b){}
};

//棋子
class Piece
{
public:
	Piece(PieceColor color) :m_color(color)
	{

	}
	~Piece()
	{

	}
	virtual void draw() {}
protected:
	PieceColor m_color;
};

//黑棋
class BlackPiece : public Piece
{
public:
	BlackPiece(PieceColor color) :Piece(color) {}
	~BlackPiece()
	{

	}
	void draw()
	{
		cout << "画一个黑棋" << endl;
	}
};

//白棋
class WhitePiece : public Piece
{
public:
	WhitePiece(PieceColor color) :Piece(color) {}
	~WhitePiece()
	{

	}
	void draw()
	{
		cout << "画一个白棋" << endl;
	}
};
如果一个棋子是一个对象,那么我们就需要存放好多的棋子(对象),
采用享元模式
//棋盘(享元工厂(伪))
class PieceBoard
{
public:
	PieceBoard(string m_Black, string m_white) : m_blackName(m_Black), m_whiteName(m_white)
	{
		m_pBlackPiece = nullptr;
		m_pWhitePiece = nullptr;
	}
	~PieceBoard()
	{
		clear();
	}
	void setPiece(PieceColor color,PiecePos pos)
	{
		if (color == BLACK)
		{
			if (m_pBlackPiece == nullptr)
			{
				m_pBlackPiece = new BlackPiece(color);
			}
			
			cout << m_blackName << "在" << pos.x << "," << pos.y << "下了一步" << endl;
			m_pBlackPiece->draw();
		}
		else
		{
			if (m_pWhitePiece == nullptr)
			{
				m_pWhitePiece = new WhitePiece(color);
			}
			cout << m_whiteName << "在" << pos.x << "," << pos.y << "下了一步" << endl;
			m_pWhitePiece->draw();
		}
		m_vecPiece.push_back(pos);
	}
	
	void clear()
	{
		delete m_pBlackPiece;
		delete m_pWhitePiece;
	}
private:
	vector<PiecePos> m_vecPiece;	//棋子
	string m_blackName;
	string m_whiteName;
	Piece* m_pBlackPiece;	//共享的黑棋
	Piece* m_pWhitePiece;	//共享的白棋
};

int main()
{
	PieceBoard pieceBoard("小黑", "小白");
	pieceBoard.setPiece(BLACK, PiecePos(44, 37));
	pieceBoard.setPiece(WHITE, PiecePos(18, 70));
	pieceBoard.setPiece(BLACK, PiecePos(1, 1));
	pieceBoard.setPiece(WHITE, PiecePos(2, 2));
	return 0;
}

享元模式结果1

3.4 工程应用

1. 数据库连接池
	数据库连接是一种稀缺资源,别人用过了的连接拿来给你继续用(共享连接)。
	就和选修抢课的时候卡的一批,
	数据库的增删查改中如果没有做好索引等就会很慢,
	如果连接数据库只有一个连接,
	然后几千号人在同一时间点抢这个连接来抢课
	就会卡爆。
	但如果给每个人一个数据库连接的账户,那更会卡爆,
	光是并发操作的问题就很麻烦了。
2. 资源池(线程池,数据库连接池)
3. 对象池

4 代理模式

场景

贷款i模式场景

定义

一点一点的读数据,
按需求读数据,
考虑访问控制
这就是代理模式的思想

结构

在这里插入图片描述

实现

//切分成log

///			故障名,故障时间,故障描述
///map<int,vector<string>> int: 故障序号,string: 故障名|故障时间|故障描述

map<int, vector<string>> Cache;

class BugManager
{
public:
	virtual void getBug() = 0;
};

class RealBugModel
{
public:
	void getBug()
	{
		ifstream in("e:\\故障列表.txt");
		if (!in)
		{
			cerr << "文件打开错误" << endl;
		}
		string line;
		int i = 0;
		while (getline(in, line))
		{
			Cache[i] = getCache(line);
			i++;
		}

		in.close();
	}

private:
	vector<string> getCache(string words)
	{
		vector<string> results;
		//切分
		istringstream ss(words);
		//没切完
		while (!ss.eof())
		{
			string word;
			getline(ss, word, '|');
			results.push_back(word);
		}
		return results;
	}
};
class ProxyBugModel
{
public:
	ProxyBugModel() :m_reload(false)
	{

	}
	void getBugs()
	{
		//真,从缓存里拿
		if (m_reload)
		{
			cout << "从缓存里获取" << endl;
			showBugs();
		}
		else
		{
			cout << "这是真实的数据" << endl;
			(new RealBugModel)->getBug();
			m_reload = true;
		}
	}
	void showBugs()
	{
		for (int i = 0; i < Cache.size(); i++)
		{
			cout << "第" << "i" << "行是:";
			for (vector<string>::iterator iter = Cache[i].begin(); iter != Cache[i].end(); iter++)
			{
				cout << (*iter) << "";

			}
			cout << endl;
		}
	}
private:
	bool m_reload;
};
int main(void)
{
	ProxyBugModel* p = new ProxyBugModel();
	for (int i = 0; i < 5; i++)
	{
		cout << "第" << i << "次请求" << endl;
		p->getBugs();
	}
	return 0;
}

实用工程技术

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱睡觉更爱学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值