设计模式学习(二)工厂模式——工厂方法模式+注册表

paw5zx Lv4

工厂方法模式的瑕疵

前一篇笔记 中我们介绍了工厂方法模式,示例的类图如下:

前文中提到

工厂方法模式把提供方原来的简单工厂类的内部判断逻辑转移到了客户端代码,这让客户端代码违反了开闭原则

下面举例说明。

示例

考虑一种情况:现在要在程序运行时,根据外部资源,动态的实例化对象。也就是说在编译期我们无法知道要实例化的对象的类型。因此在实例化的过程中,就需要加以判断。

例如,在我的例子中,要根据连接到主机的相机来实例化相机对象,那么客户端(使用工厂方法创建实例的一方)使用工厂方法模式创建对象的时候,代码可能是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//运行时确定数组大小,且确定后不可改变
auto camera_devices_ = std::make_unique<std::shared_ptr<CameraDevice>[]>(onlined_camera_num_);

for(int i = 0; i < onlined_camera_num_; ++i)
{
std::shared_ptr<CameraDeviceFactory> factory;
if("Sick" == camera_name[i]) //camera_name[i]中元素是提前获取的与连接的相机对应的供应商名称
factory = std::make_shared<SickCameraFactory>();
else if("Basler" == camera_name[i])
factory = std::make_shared<BaslerCameraFactory>();
else if("Huaray" == camera_name[i])
factory = std::make_shared<HuarayCameraFactory>();
camera_devices_[i] = factory->CreateCamera();
}

虽然作为提供方,使用工厂方法模式遵循了开闭原则,即当有新类型的时候,无需修改现有的代码,只需新加产品类和对应工厂类即可。但是对于客户端来说,当需要实例化的类型数量增加时,就需要新增else if去适配,这违反了开闭原则,将使客户端代码变得冗长且难以维护。

注册表

为了解决上面问题,我们可以实现一个类型的注册表,允许动态创建对象。这种方法通过将关键字映射到构造函数指针,使得可以根据字符串名称动态地实例化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#ifndef Reflection_H
#define Reflection_H

#include <map>
#include <string>

template <typename T, typename... ArgType>
void* CreateInstance(ArgType... args)
{
return new T(args...);
}

//需要反射的类使用此宏注册,将关键词与构造函数关联
//此注册过程会在main函数执行之前完成
//因为宏定义了一个静态变量,会在main函数执行之前的静态初始化阶段被初始化
#ifndef ReflectRegister
#define ReflectRegister(identifier, class_name, ...) \
static bool __type##class_name = Object::Register(identifier, (void*)CreateInstance<class_name, ##__VA_ARGS__>);
#endif

class Object
{
public:
template <typename BaseClass, typename... ArgType>
static BaseClass *CreateObject(const std::string &vendor_name, ArgType... args)
{
using CreateFactory = BaseClass *(*)(ArgType...);
auto& class_map = GetStaticFuncMap();
auto iter = class_map.find(vendor_name);
if (iter == class_map.end())
{
return nullptr;
}
else
{
return reinterpret_cast<CreateFactory>(class_map[vendor_name])(args...);
}

}

//向map中注册关键字和类的构造函数
static bool Register(const std::string &vendor_name, void *ctor_ptr)
{
GetStaticFuncMap()[vendor_name] = ctor_ptr;
return true;
}

private:
//获取全局唯一的map
//map记录了关键字和类的构造函数的映射关系
static std::map<std::string, void*>& GetStaticFuncMap()
{
static std::map<std::string, void*> class_map_;
return class_map_;
}
};

#endif //Reflection_H

在具体相机工厂中,我们可以使用ReflectRegister注册此类(以Basler相机为例,其余类似):

1
2
3
4
5
6
7
8
9
10
class BaslerCameraDeviceFactory : public CameraDeviceFactory
{
public:
std::shared_ptr<CameraDevice> CreateCameraDevice() override
{
return std::make_shared<BaslerCameraDevice>();
}
};

ReflectRegister("Basler", BaslerCameraDeviceFactory);

好了,现在回头再看客户端使用工厂方法模式创建对象的代码,就可以简化为:

1
2
3
4
5
6
7
8
9
10
11
12
13
//运行时确定数组大小,且确定后不可改变
auto camera_devices_ = std::make_unique<std::shared_ptr<CameraDevice>[]>(onlined_camera_num_);

for(int i = 0; i < onlined_camera_num_; ++i)
{
auto p_factory = Object::CreateObject<CameraDeviceFactory>(camera_name[i]);//camera_name[i]中元素是提前获取的与连接的相机对应的供应商名称
if (!p_factory)
continue;
else
camera_devices_[i] = p_factory->CreateCameraDevice();

delete p_factory;
}
  • 标题: 设计模式学习(二)工厂模式——工厂方法模式+注册表
  • 作者: paw5zx
  • 创建于 : 2024-06-03 17:36:00
  • 更新于 : 2024-08-22 13:51:47
  • 链接: https://paw5zx.github.io/design-pattern-2-factory-method-reflect/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
设计模式学习(二)工厂模式——工厂方法模式+注册表