反射实现简单工厂模式。
前文《简单工厂模式与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。
注意事项
需要有LanguageTypeToString()
函数将枚举转换为字符串,而且枚举不能用c++11新特性enum class
,只能用enum
;
这里如果用switch-case
或if-else
得到类名,那还不如直接用普通的简单工厂方式;
类需要继承自QObject
, 且构造函数前面需要加Q_INVOKABLE
宏;
需要使用qRegisterMetaType
函数注册类;
注意QMetaObject::newInstance
函数传参时Q_ARG
的用法。
C++语言没有反射机制的,虽然借助Qt实现了但是实现起来并没有C#优雅、简单,从注意事项可以看出,用起来还是很麻烦的,期待后期通过反射+模板,更简单的实现,参见Qt元对象Meta−Object系统