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
,其有两个派生类Cat
和Dog
。其定义分别如下:
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
标签:std,name,Factory,C++,工厂,简易,Cat,include,string
From: https://www.cnblogs.com/cherish-/p/17583887.html