作为项目开发经理,给大家安排工作的时候,希望每个人开发的功能模块之间的耦合越小越好,最好不要出现以下场景:A等B提供的接口,C等B提供的业务模块。一旦出现这样的场景也就是预示着整个项目出现停顿或进度缓慢的情况。这时插件化开发就能很好的解决这样的问题。
每个人都是单独的开发自己的业务功能模块,尽量少的依赖于别人提供的功能,等大家完成了,把每个人开发的功能组合到一起程序就能很好的在一起工作了。
以上是插件化开发的理想场景。
要想实现插件化首先要对业务场景非常熟悉,能够对整个软件做很好的模块划分,还要定义好每个模块之间的对外接口。
有这样一个场景:现在需要开发一个绘制模块,根据需要来绘制不同的图形,三角形、矩形、圆形等,有可能还会绘制更复杂的图形。如何实现插件化的开发呢?
1.首先定义一个绘制的接口:
新建一个工程,这个工程就是只有一个类,就是绘制基类,所有其他的具体的绘制都是这个绘制基类的子类:
drawgraph_global.h
#pragma once
#include <QtCore/qglobal.h>
#ifndef BUILD_STATIC
# if defined(DRAWGRAPH_LIB)
# define DRAWGRAPH_EXPORT Q_DECL_EXPORT
# else
# define DRAWGRAPH_EXPORT Q_DECL_IMPORT
# endif
#else
# define DRAWGRAPH_EXPORT
#endif
DrawGraph.h
#pragma once
#include "drawgraph_global.h"
class DRAWGRAPH_EXPORT DrawGraph {
public:
DrawGraph();
virtual void init();//初始化绘制参数
virtual void draw();//开始绘制
};
DrawGraph.cpp
#include "DrawGraph.h"
DrawGraph::DrawGraph() {
}
void DrawGraph::init() {
}
void DrawGraph::draw() {
}
上面新建的工程只有三个文件,里面只定义了一个绘制基类,这个基类只提供了两个绘制接口,一个是初始换绘制参数接口:
virtual void init();
一个是具体的绘制接口:
virtual void draw();
以后所有关于绘制的子类都是要实现者两个接口的。
2.实现绘制矩形接口
新建一个DLL工程,在这个工程中新建一个绘制矩形的类,必须继承绘制基类。
drawrect_global.h
#pragma once
#include <QtCore/qglobal.h>
#ifndef BUILD_STATIC
# if defined(DRAWRECT_LIB)
# define DRAWRECT_EXPORT Q_DECL_EXPORT
# else
# define DRAWRECT_EXPORT Q_DECL_IMPORT
# endif
#else
# define DRAWRECT_EXPORT
#endif
DrawRect.h
#pragma once
#include "drawrect_global.h"
#include "../DrawGraph/DrawGraph.h"
class DRAWRECT_EXPORT DrawRect:public DrawGraph {
public:
DrawRect();
virtual void init()override;
virtual void draw()override;
};
extern "C" {
DRAWRECT_EXPORT DrawRect* getObj();
}
DrawRect.cpp
#include "DrawRect.h"
#include <QDebug>
DrawRect::DrawRect() {
}
void DrawRect::init() {
qDebug() << QStringLiteral("初始化绘制矩形参数");
}
void DrawRect::draw() {
qDebug() << QStringLiteral("根据设置的参数绘制矩形");
}
DRAWRECT_EXPORT DrawRect* getObj() {
return new DrawRect;
}
3.实现绘制圆形接口
新建一个工程,同样新建一个类,继承自绘制基类;
drawcircle_global.h
#pragma once
#include <QtCore/qglobal.h>
#ifndef BUILD_STATIC
# if defined(DRAWCIRCLE_LIB)
# define DRAWCIRCLE_EXPORT Q_DECL_EXPORT
# else
# define DRAWCIRCLE_EXPORT Q_DECL_IMPORT
# endif
#else
# define DRAWCIRCLE_EXPORT
#endif
DrawCircle.h
#include "../DrawGraph/DrawGraph.h"
class DRAWCIRCLE_EXPORT DrawCircle:public DrawGraph{
public:
DrawCircle();
virtual void init()override;
virtual void draw()override;
};
extern "C" {
DRAWCIRCLE_EXPORT DrawCircle* getObj();
}
DrawCircle.cpp
#include "DrawCircle.h"
#include <QDebug>
DrawCircle::DrawCircle() {
}
void DrawCircle::init() {
qDebug() << QStringLiteral("初始化绘制圆参数");
}
void DrawCircle::draw() {
qDebug() << QStringLiteral("根据设置的参数绘制圆");
}
DRAWCIRCLE_EXPORT DrawCircle* getObj() {
return new DrawCircle;
}
以上就是实现两种图形矩形和圆形的绘制,编译一下,均生成相应的DLL文件。
大家已经注意到了,为什么到处符号中会使用extern “C”?这个问题在上一篇文章总已经说明过了。
4.调用插件
首先定义到处符号:
typedef DrawGraph* (*getDrawObj)();
具体的调用,首先调用绘制圆的插件:
QString dllName = "DrawCircle";//注意:插件的名称
dllName += ".dll";
QLibrary* draw = new QLibrary(dllName);
if (draw->load()){
getDrawObj drawObj = (getDrawObj)draw->resolve("getObj");
DrawGraph* obj = drawObj();
obj->init();
obj->draw();
}
运行一下,输出信息:
"初始化绘制圆参数"
"根据设置的参数绘制圆"
修改一下,现在要调用绘制矩形的插件:
只需要把:插件的名称修改一下就可以了,其他的地方不用变。
QString dllName = "DrawRect";
dllName += ".dll";
QLibrary* draw = new QLibrary(dllName);
if (draw->load()){
getDrawObj drawObj = (getDrawObj)draw->resolve("getObj");
DrawGraph* obj = drawObj();
obj->init();
obj->draw();
}
编译输出:
"初始化绘制矩形参数"
"根据设置的参数绘制矩形"
如果是插件的功能需要修改,只要把DLL重新修改一下就可以了,接口不变,客户端代码也是不需要改变的。
以上是手工实现插件的不同调用,也可以把插件的名称放到配置文件中去,然后一次性的读入到内存中,在根据不同的需求调用不同的功能插件。