简单工厂模式与Qt类反射2

反射实现简单工厂模式。

前文《简单工厂模式与Qt类反射1》中简单工厂模式缺点3说:这不就是LanguageShow()函数把违反开闭原则的锅甩给了Factory::CreateObject()函数吗?,本文通过反射解决该问题。

之前写过一段时间WPF,受WPF大佬NaBian的控件库 HandyControl | HandyOrg github.com 启发,知道了C#语言可以通过反射机制实现简单工厂模式。

但是C++语言本身没有反射机制,只好借用Qt的反射来实现。

反射实现简单工厂

language.h

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <QObject>
#include <QMetaEnum>
#include <iostream>

using namespace std;

class MetaEnums : public QObject
{
Q_OBJECT
public:

explicit MetaEnums(QObject *parent = nullptr) : QObject(parent) {}
enum LanguageType
{
C,
CPP,
Python
};

Q_ENUM(LanguageType)

QString LanguageTypeToString(LanguageType type)
{
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageType>();
return QString(metaEnum.valueToKey(type)) + "Language";
}
};

class LanguageBase : public QObject
{
Q_OBJECT
public:
explicit LanguageBase(QObject *parent = nullptr) : QObject(parent) {}
virtual void Show() = 0;
};

class CLanguage : public LanguageBase
{
Q_OBJECT
public:
Q_INVOKABLE explicit CLanguage(QObject *parent = nullptr) : LanguageBase(parent) {}
void Show()
{
cout << "I'm C" << endl;
}
};

class CPPLanguage : public LanguageBase
{
Q_OBJECT
public:
Q_INVOKABLE explicit CPPLanguage(QObject *parent = nullptr) : LanguageBase(parent) {}
void Show()
{
cout << "I'm CPP" << endl;
}
};

class PythonLanguage : public LanguageBase
{
Q_OBJECT
public:
Q_INVOKABLE explicit PythonLanguage(QObject *parent = nullptr) : LanguageBase(parent) {}
void Show()
{
cout << "I'm Python" << endl;
}
};

static int cTypeId = qRegisterMetaType<CLanguage*>();
static int cppTypeId = qRegisterMetaType<CPPLanguage*>();
static int pythonTypeId = qRegisterMetaType<PythonLanguage*>();

class Factory
{
public:
LanguageBase* CreateObject(MetaEnums::LanguageType type)
{
MetaEnums metaEnums;
auto className = metaEnums.LanguageTypeToString(type) + "*";

int classTypeID = QMetaType::type(className.toLocal8Bit().data());
const QMetaObject *theMetaObject = QMetaType::metaObjectForType(classTypeID);
if (theMetaObject == nullptr)
return nullptr;

QObject *obj = theMetaObject->newInstance(Q_ARG(QObject*, nullptr));
LanguageBase *languageBase = qobject_cast<LanguageBase*>(obj);
return languageBase;
}
};

其他函数同上。

Factory::CreateObject()函数终于不用背违反开闭原则的锅了。

需要有几个注意事项,否则可能一直Create不出Object。

注意事项

  1. 需要有LanguageTypeToString()函数将枚举转换为字符串,而且枚举不能用c++11新特性enum class,只能用enum;

    这里如果用switch-caseif-else得到类名,那还不如直接用普通的简单工厂方式;

  2. 类需要继承自QObject, 且构造函数前面需要加Q_INVOKABLE宏;

  3. 需要使用qRegisterMetaType函数注册类;

  4. 注意QMetaObject::newInstance函数传参时Q_ARG的用法。

C++语言没有反射机制的,虽然借助Qt实现了但是实现起来并没有C#优雅、简单,从注意事项可以看出,用起来还是很麻烦的,期待后期通过反射+模板,更简单的实现,参见Qt元对象Meta−Object系统