返回顶部

C++工厂模式简易实现

C++工厂模式简易实现

引言:动态绑定是面向对象编程的重要功能,但C++目前还没有纳入标准库的反射机制,所以为了更方便的动态构造对象,使得通过配置文件的方式改变派生类对象,而不需要去修改代码,所以可以使用工厂这一常见的设计模式,来完成类对象的动态构造。

基于C++11的新特性和模板,实现一个简易的类对象构造工厂,本文实现的工厂产生的类对象都是独立的。(即非单例工厂)

首先,考虑到在一个程序中,始终应该只有一个工厂对象,故我们采用单例模式来实现工厂,其代码如下

#pragma once

#include <unordered_map>
#include <memory>
#include <functional>
#include <string>

// 第一个参数是基类名称 可变参数是构造器的参数列表
template<class Base, class... Args>
class Factory {
public:
	// 禁止外部拷贝和赋值
	Factory(const Factory&) = delete;
	const Factory& operator = (const Factory&) = delete;
	// 获取工厂实例
	static Factory& Instance() {
        // C++11 保证static对象在构造的时候是线程安全的
		static Factory<Base, Args...>* instance = new Factory<Base, Args...>();
		return *instance;
	}
	// 向工厂注册类及其构造器
	void Register(const std::string& name, std::function<std::shared_ptr<Base>(Args...)> constructor) {
		_factory[name] = constructor;
		return;
	}
	// 通过名字构造对象
	std::shared_ptr<Base> Construction(const std::string& name, Args... args) {
		return _factory.find(name) == _factory.end() ? nullptr :
			_factory[name](std::forward<Args>(args)...);
	}
private:
	Factory() = default;
	~Factory() = default; // 禁止外部析构
	// 类名->构造器
	std::unordered_map<std::string, std::function<std::shared_ptr<Base>(Args...)>> _factory;
};

然后为了减少向工厂注册类对象的重复工作量,将该操作进行封装,封装后的类对象如下:

#pragma once

#include "Factory.h"

// 辅助注册类 第一个参数是基类 第二个参数是 派生类 
// 可变参数是派生类构造函数所需要的参数
template<class Base, class Impl, class... Args>
class Register {
public:
	// name是对象的名字,也是工厂中用于映射的key
	explicit Register(const std::string& name) {
		Factory<Base, Args...>& factory = Factory<Base, Args...>::Instance();
		factory.Register(name, [](Args... args) {
			return std::make_shared<Impl>(std::forward<Args>(args)...);
			});
	}
};

以上就是C++工厂的实现,下面来进行测试。

假设基类为Animal,其有两个派生类CatDog。其定义分别如下:

Animal.h内容:

#pragma once

#include <iostream>
#include <string>
#include "Factory.h"


// 基类 声明为纯虚函数
class Animal {
public:
	Animal() = default;
	virtual ~Animal() = default;
	virtual std::string getName() = 0;
private:
};

Dog.h内容:

#pragma once

#include "Animal.h"
#include "Register.h"

class Dog : public Animal {
public:
	Dog() = default;
	Dog(const std::string& _name) : name(_name) {}
	std::string getName() override;
	~Dog() = default;
private:
	std::string name;
};

Dog.cpp内容:

#include "Dog.h"

std::string Dog::getName() {
	return name;
}

namespace {
	// 注册语句建议不要放在.h文件中,可能会导致在编译的时候出现重名
	// 放在.cpp文件并放入匿名namespace中可以将作用域限定在该cpp文件
	// 不会与其他文件产生冲突
	// 本质是定义了一个名字为 _ 的Register对象 因为在cpp文件中
	// 且放入匿名namespace,所以Cat.cpp中同样可以定义一个名字为 _ 的
	// Register对象
	Register<Animal, Dog, std::string> _("Dog");
}

Cat.h内容:

#pragma once

#include "Animal.h"
#include "Register.h"


class Cat : public Animal {
public:
	Cat() = default;
	Cat(const std::string& _name) : name(_name) {}
	std::string getName() override;
	~Cat() = default;
private:
	std::string name;
};

Cat.cpp内容

#include "Cat.h"


std::string Cat::getName() {
	return name;
}

namespace {
	// 同Dog注释
	Register<Animal, Cat, std::string> _("Cat");
}

最后,进行测试,main.cpp文件内容如下:

#include <iostream>
#include <string>

#include "Factory.h"
#include "Cat.h"
#include "Dog.h"

namespace {
	using std::cout;
	using std::endl;
	using std::string;
}

int main() {
	std::shared_ptr<Animal> animal1 = Factory<Animal, std::string>::Instance().Construction("Cat", "I am cat");
	std::shared_ptr<Animal> animal2 = Factory<Animal, std::string>::Instance().Construction("Dog", "I am dog");
	if (animal1 != nullptr && animal1.get() != nullptr) {
		std::cout << animal1->getName() << std::endl;
	}
	if (animal1 != nullptr && animal2.get() != nullptr) {
		std::cout << animal2->getName() << std::endl;
	}
	return 0;
}

输出如下:

I am cat
I am dog
posted @ 2023-08-05 12:45  cherish-lgb  阅读(42)  评论(0编辑  收藏  举报