设计模式学习(十四)————抽象工厂模式(使用Qt框架的反射技术——根据字符串动态创建类来实现)
来源:互联网 发布:java中级工程师学校 编辑:程序博客网 时间:2024/05/29 13:59
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类!
这个例子也可以用简单工厂模式+反射+读取配置文件来完成,这样更加简洁!!!
普通的抽象工厂模式
下面通过一个模拟访问数据库的例子来进行说明:
#ifndef USER#define USER#include <QString>#include <QtDebug>class User{public: User() = default; int getID(){return _id;} void setID(int id){_id = id;} QString getName(){return _name;} void setName(QString name){_name = name;}private: int _id; QString _name;};class IUser{public: virtual void Insert(User user) = 0; virtual User* getUser(int id) = 0;};class SqlServerUser final: public IUser{public: void Insert(User user) override { Q_UNUSED(user); qDebug() <<"在SQL Server中给User表增加一条记录"; } User* getUser(int id) override { Q_UNUSED(id); qDebug() <<"在SQL Server中根据id得到User表一条记录"; return NULL; //这里为了做例子,所以没有真正返回业务需要的指针,只是返回空指针做示范 }};class AccessUser final: public IUser{public: void Insert(User user) override { Q_UNUSED(user); qDebug() <<"在Access中给User表增加一条记录"; } User* getUser(int id) override { Q_UNUSED(id); qDebug() <<"在Access中根据id得到User表一条记录"; return nullptr; //这里为了做例子,所以没有真正返回业务需要的指针,只是返回空指针做示范 }};class Department{private: QString _departmentName;};class IDepartment{public: virtual void Insert(Department department) = 0;};class SqlServerDepartment:public IDepartment{public: void Insert(Department department) override { Q_UNUSED(department); qDebug() <<"在SQL Server中给Department表增加一条记录"; }};class AccessDepartment:public IDepartment{public: void Insert(Department department) override { Q_UNUSED(department); qDebug() <<"在Access中给Department表增加一条记录"; }};class IFactory{public: virtual IUser* CreateUser() = 0; virtual IDepartment *CreateDepartment() = 0;};class SqlServerFactory: public IFactory{public: IUser* CreateUser() override { return new SqlServerUser(); } IDepartment* CreateDepartment() override { return new SqlServerDepartment(); }};class AccessFactory: public IFactory{public: IUser* CreateUser() override { return new AccessUser(); } IDepartment* CreateDepartment() override { return new AccessDepartment(); }};#endif // USER#include "user.h"#define use_SQLServerint main(int argc, char *argv[]){ Q_UNUSED(argc); Q_UNUSED(argv); User *user = new User(); Department *department = new Department();#ifdef use_SQLServer IFactory *factory = new SqlServerFactory();#else IFactory *factory = new AccessFactory();#endif IUser *iu = factory->CreateUser(); IDepartment *id = factory->CreateDepartment(); id->Insert(*department); iu->getUser(1); iu->Insert(*user); return 0;}
这样的实现就可以满足同样的代码,如果需要通过不同的数据库实现同样的功能的话,只需要在开头的#define use_SQLServer
注释掉和不注释掉两种方法即可,大大的增加的软件的复用性,减少了相关的修改操作。
简单工厂模式改进的抽象工厂模式
这里进行抽象工厂模式的使用时,对于使用哪种数据库来说,还是必须在主函数里面通过新建不同的数据库类来实现不同数据库的访问,如果我想要把这个逻辑也在逻辑代码中实现,就可以使用简单工厂模式改进抽象工厂模式,代码如下:
#ifndef DATAACCESS_H#define DATAACCESS_H#include "user.h"#include <QString>class DataAccess{public: static IUser* CreatUser() { IUser* result = nullptr; if(_db == "Sqlserver") result = new SqlServerUser(); else if(_db == "Access") result = new AccessUser(); return result; } static IDepartment* CreatDepartment() { IDepartment* result = nullptr; if(_db == "Sqlserver") result = new SqlServerDepartment(); else if(_db == "Access") result = new AccessDepartment(); return result; }private: static const QString _db; //注意:静态非整型数据成员,必须定义为const,且在类外初始化};//const QString DataAccess::_db = QString::fromUtf8("Sqlserver");const QString DataAccess::_db = QString::fromUtf8("Access"); //把上面那句话取消注释,本行加上注释,就是使用sql服务的相关操作
这样主函数改为:
#include "user.h"#include "dataaccess.h"int main(int argc, char *argv[]){ Q_UNUSED(argc); Q_UNUSED(argv); User *user = new User(); Department *department = new Department(); IUser *iu = DataAccess::CreatUser(); IDepartment *id = DataAccess::CreatDepartment(); id->Insert(*department); iu->getUser(1); iu->Insert(*user); return 0;}
这样,在主函数中使用DataAccess
的简单工厂来创建出对应的类的实例即可。然后如果需要改的数据库的类型,直接在dataaccess.h
头文件中将静态变量_db
的内容改动一下即可。
在简单工厂模式中使用反射技术来改进的抽象工厂模式
下面的内容是抽象工厂模式的第三重境界。前两重分别是:普通的抽象工厂模式的使用、简单工厂模式改进抽象工厂模式的使用。而第三重境界就是:在简单工厂模式中使用反射技术来改进抽象工厂模式。话不多说,放上代码:
#ifndef DATAACCESS_H#define DATAACCESS_H#include "user.h"Q_DECLARE_METATYPE(SqlServerUser)Q_DECLARE_METATYPE(AccessUser)Q_DECLARE_METATYPE(SqlServerDepartment)Q_DECLARE_METATYPE(AccessDepartment)void RegisterMetaType(){ qRegisterMetaType<SqlServerUser>(); qRegisterMetaType<AccessUser>(); qRegisterMetaType<SqlServerDepartment>(); qRegisterMetaType<AccessDepartment>();}class DataAccess{public: static IUser* CreatUser() { IUser* result = nullptr; QString className = _db + "User"; int id = QMetaType::type(className.toLatin1()); if (id != QMetaType::UnknownType) result = static_cast<IUser*>(QMetaType::create(id)); else qDebug()<<"没有在Qt中注册成功该类"; return result; } static IDepartment* CreatDepartment() { IDepartment* result = nullptr; QString className = _db + "Department"; int id = QMetaType::type(className.toLatin1()); if (id != QMetaType::UnknownType) result = static_cast<IDepartment*>(QMetaType::create(id)); else qDebug()<<"没有在Qt中注册成功该类"; return result; }private: static const QString _db; //注意:静态非整型数据成员,必须定义为const,且在类外初始化};#ifdef use_SQLServerconst QString DataAccess::_db = QString::fromUtf8("SqlServer");#elseconst QString DataAccess::_db = QString::fromUtf8("Access"); //把上面那句话取消注释,本行加上注释,就是使用sql服务的相关操作#endif#endif // DATAACCESS_H
由于我使用了Qt的反射技术,这里讲解一下。虽然C++本身不向java或者C#那样可以有库自带反射实现的方法来实现通过类名动态的创建类,但是Qt框架的元系统可以使这一过程简化,如果想要动态的在运行时根据字符串的名字创建类,只需要完成以下三步即可:
第一步:将需要使用的类通过Q_DECLARE_METATYPE
注册,如:Q_DECLARE_METATYPE(CExample)
。注意:(1)需要构造的类必须提供公用的构造函数、拷贝构造函数和析构函数,当然如果没有复杂的需要深拷贝的数据,使用编译器默认提供的也行。(2)以下类无需使用该宏即可自动的注册到Qt元系统中:
a.继承自QObject的类;
b.QList<T>, QVector<T>, QQueue<T>, QStack<T>, QSet<T> 和 QLinkedList<T>
这些数据结构中T是 被注册过的类型时;
c.QHash<T1, T2>, QMap<T1, T2> 和 QPair<T1, T2>
这些数据结构中T1和T2是 被注册过的类型时;
d.QPointer<T>, QSharedPointer<T>, QWeakPointer<T>
, 这些智能指针中T是被注册过的类型时;
e.通过Q_ENUM
或者 Q_FLAG
注册的枚举数据;
f.具有一个Q_GADGET
宏的类。
第二步:如果需要动态的根据类名创建该类,需要在main函数中通过qRegisterMetaType
注册,例如:qRegisterMetaType<CExample>();
注意:如果仅仅要在QVariant
中使用该类,就不需要这一步了。
第三步:通过int id = QMetaType::type(该类名字的字符串转换为QByteArray或者直接const char*)
,然后使用QMetaType::create(id)
返回一个新的该类的指针。例如:
QString className = "CExample";int id = QMetaType::type(className.toLatin1()); //或者int id = QMetaType::type("CExample"); CExample* result = static_cast<CExample*>(QMetaType::create(id));
通过上面三步,就可以动态的通过类名创建类啦,我上面的DATAACCESS_H
文件也是这样用的,接下来,在main函数中的代码如下:
#define use_reflect //如果不使用反射,注释掉这句话#define use_SQLServer //如果使用AccessServer,注释掉这句话#include "user.h"#include "dataaccess.h"int main(int argc, char *argv[]){ Q_UNUSED(argc); Q_UNUSED(argv);#ifdef use_reflect RegisterMetaType();#endif User *user = new User(); Department *department = new Department(); IUser *iu = DataAccess::CreatUser(); IDepartment *id = DataAccess::CreatDepartment(); id->Insert(*department); iu->getUser(1); iu->Insert(*user); return 0;}
这样就实现了在简单工厂模式中使用反射技术来改进抽象工厂模式。通过这样的改进,我们可以发现当我们需要使用简单工厂构造多个新的类的时候,只需要修改那个需要变化的字符串,然后其他地方都不需要改动,就可以实现对不同数据库的调用,真的是非常方便,非常好用,设计模式实在是太棒了!
最后放上源码地址:
https://github.com/Dongzhixiao/designMode_qt/tree/master/visitDatabase_Abstract_Factory_15
参考链接:
http://kunalmaemo.blogspot.com/2010/07/creating-class-dynamically-from-its.html
http://www.mimec.org/node/350
http://stackoverflow.com/questions/359237/why-does-c-not-have-reflection
http://stackoverflow.com/questions/41453/how-can-i-add-reflection-to-a-c-application
http://doc.qt.io/qt-5/qmetatype.html
- 设计模式学习(十四)————抽象工厂模式(使用Qt框架的反射技术——根据字符串动态创建类来实现)
- 设计模式学习—抽象工厂模式
- Qt设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- 设计模式(对象创建)——抽象工厂模式
- 设计模式学习(创建型模式)—抽象工厂模式(Abstract Factory)
- 设计思想学习—抽象工厂模式
- 创建型设计模式——抽象工厂模式
- java创建型设计模式——抽象工厂模式
- 设计模式——抽象工厂之反射“+”
- 设计模式学习笔记——简单工厂模式、工厂方法模式、抽象工厂模式
- 创建模式—抽象工厂模式
- 创建型模式—抽象工厂模式
- 设计模式——抽象工厂模式
- 设计模式——抽象工厂模式
- 设计模式——抽象工厂模式
- 设计模式——抽象工厂模式
- 设计模式——抽象工厂模式
- 设计模式——抽象工厂模式
- 表达式的3种表示方式
- Linux须掌握的10个技术点
- 泛型擦除
- 开始使用Logstash
- 线程的问题
- 设计模式学习(十四)————抽象工厂模式(使用Qt框架的反射技术——根据字符串动态创建类来实现)
- Dialog
- VC基础
- utf-8和Unicode的纠结
- 图说C++对象模型:对象内存布局详解
- 20160918iOS音频开发之资源(AVAsset)与元数据,解决获取资源属性问题(三)
- 《GPU高性能编程CUDA实战》学习笔记(十一)
- Jvm内存分配
- Android 仿IOS解锁锁屏文字抖动效果