C++设计模式-创建型设计模式:工厂模式

之前提到了简单工厂模式,其核心思想是客户端与接口完全解耦。

而工厂模式是对简单工厂的进一步提炼与总结。

工厂模式的介绍-定义、结构、参考实现、场景

场景

  • 实现一个导出数据的应用框架,来让客户选择数据导出的方式,并真正执行数据导出
  • 通常这种系统,在导出数据上会与一些与约定的方式,比如导出为csv格式,数据库备份形式,excel格式,XML格式等

从封装的角度来讲,我们希望导出的数据数据的业务功能创建ExportFileApi的具体实例,目前只知道接口,该怎么办?

答:让子类决定实例化某一个类,实现类的实例化的延迟。

工厂模式图大致如下:

话不多说,先上代码,注意看注释:

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

class ExportFileApi {
public:
	virtual bool exportData(string data) = 0;
protected:
	ExportFileApi() {}
};

//具体化子类
class ExportTextFile :public ExportFileApi {
public:
	bool exportData(string data) {
		cout << "正在导出数据" << data << "到csv文件" << endl;
		return true;
	}
};

//生成数据到数据库
class ExportDB :public ExportFileApi {
public:
	bool exportData(string data) {
		cout << "正在导出数据" << data << "数据库" << endl;
		return true;
	}
};

//实现一个ExportOperate,导出数据的业务功能
//这也是接口,具体的工作交给子类实现
class ExportOperate {
public:
	bool exportData(string data) {
		ExportFileApi* pApi = factoryMethod();
		return pApi->exportData(data);
	}
protected:
	virtual ExportFileApi* factoryMethod() = 0;
};

//具体的实现对象,完成导出工作
class ExportTextFileOperate :public ExportOperate {
	//不想让外部来访问
protected:
	ExportFileApi* factoryMethod() {
		return new ExportTextFile();
	}
};

class ExportDBOperate :public ExportOperate {
protected:
	ExportFileApi* feactoryMethod() {
		return new ExportDB;
	}
};

int main() {
	ExportOperate* pOperate = new ExportTextFileOperate();
	pOperate->exportData("666");
	return 0;
}

 对于此例的UML工厂模式的UML:

定义

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

工厂模式的应用案例与思考-IOC/DI(依赖注入,控制反转)

依赖注入:应用程序依赖容器创建并注入它所需要的外部资源

控制反转:容器控制应用程序,由容器反向的向Application注入程序所需要的

降低A和B的耦合,交由IOC容器去管理。

同时这种思想适用于很多个对象之间的管理,就比如一个老师要管理50甚至上百个学生,需要一个点名册,记录学号等信息,来管理学生。这个点名册就是IOC。

具体看一个例子:

#include <string>
#include <map>
#include <memory>
#include <functional>
#include <iostream>
using namespace std;

template <class T>
class IocContainer {
public:
	IocContainer() {}
	~IocContainer()
	{

	}

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

	//根据唯一的标识查找对应的构造函数
	T* Resolve(string strKey) {
		if (m_createMap.find(strKey) == m_createMap.end()) {
			return nullptr;
		}
		std::function<T* ()>function = m_createMap[strKey];
		return function();
	}

	//创建智能指针
	std::shared_ptr<T>ResolveShared(string strKey) {
		T* ptr = Resolve(strKey);
		return std::shared_ptr<T>(ptr);
	}
private:
	void RegisterType(string strKey, std::function<T* ()>creator) {
		if (m_createMap.find(strKey) != m_createMap.end()) {
			throw std::invalid_argument("已经存在这个key了");
		}
		m_createMap.emplace(strKey, creator);
	}
private:
	map<string, std::function<T* ()>>m_createMap;
};


struct ICar {
	virtual ~ICar() {}
	virtual void test()const = 0;
};

struct Bus:ICar
{
	Bus() {}
	void test()const { cout << "Bus test" << endl; }
};

struct Track :ICar
{
	Track() {}
	void test()const { cout << "Track test" << endl; }
};

int main() {
	IocContainer<ICar>carIOC;
	carIOC.RegisterType<Bus>("bus");
	carIOC.RegisterType<Track>("track");

	std::shared_ptr<ICar>bus = carIOC.ResolveShared("bus");
	bus->test();

	std::shared_ptr<ICar>track = carIOC.ResolveShared("track");
	track->test();

	return 0;
}

工厂模式的本质-依赖倒置-让子类选择实现

  • 依赖倒置原则:要依赖抽象,不要依赖于具体类,不能让高层组件依赖于底层组件,而且不管高层组件还是底层组件,都应该依赖于抽象

何时选用工厂模式?

  • 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类去实现
  • 如果一个类本身就希望由它的子类来创建所需的对象时候,就应该使用工厂模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值