music unfamous original game design efficient software wtf
life ui algorithm fix programming

插件式软件架构实践

作者:trinity  C/C++    2015-3-3  标签:  design 

一,插件式架构

    插件式架构是一种软件构建方案,部分功能并不提供具体的实现,或者仅仅提供一种或有限几种实现,仅仅提供编程接口,其他插件以某种功能表现实现这些接口,以扩展软件的功能。

    这种设计通常面向两种场景:

    1,软件发行方便于维护软件逻辑,维护软件扩展升级。

    2,软件发行方便于其他人或团队扩展该软件功能。

二,优点

    1,天然解耦软件逻辑和代码结构,便于维护。

    2,天然模块划分清晰,便于同步开发。

    3,提供良好的扩展性。

    4,热更换,热升级。

三,著名的插件式架构实例

    Eclipse,Jquery,诸多播放器。

    当然以这种架构实现的软件有很多,恕不一一列举。

四,C/C++的示例

     从技术上来讲,可以采用 dll,COM,考虑到其本质思想是一致的,COM多了一个注册的过程,就采用dll 的方式。

     首先定义一个接口,每一个插件dll 都实现这个接口中的方法。

#pragma once

#include "ImgInfo.h"

#define MOLEAPI extern "C" __declspec(dllexport)

/** 根据 ImgInfo 结构体信息,生成图片全路径,保存图片文件
 @base:生成图片保存的目录
 @buf: ImgInfo 结构体
 @len: 结构体长度
 @img: 图片raw 数据
 @img_len: 图片数据长度
 @fmted: 格式化后的图片文件全路径
 @fmtlen: 长度
 @return: 0 表示成功
*/
MOLEAPI int format(LPCTSTR base,void* buf,int len,void* img,int img_len,wchar_t*fmted,int fmtlen);
/**! 根据某个图片文件,解析出ImgInfo 结构体
  注意:解析后的 ImgInfo 一般不会和 format 之前的对应,会有信息损失
  @file:
  @img:
  @img_len:
  @return:
*/
MOLEAPI int _format_(LPCTSTR file,void*img,int img_len);
//dll 版本
MOLEAPI unsigned int version();
// dll 描述信息
MOLEAPI wchar_t*  description();

插件1

int format(LPCTSTR base,void* buf,int len,void* img,int img_len,wchar_t*fmted,int fmtlen)
{
	if(len < sizeof(ImgInfo)){
		return -1;
	}

	ImgInfo *ii = (ImgInfo*)buf;
	if(ii->size != sizeof(ImgInfo)){
		return -2;
	}

	TCHAR dir[MAX_PATH]={0};
	TCHAR file[MAX_PATH]={0};
	swprintf_s(dir,L"%s\\%s(%s)\\%04d%02d%02d\\%02d",
		base,ii->szDevCode,ii->szDevIP,ii->stTimeGrab.wYear,ii->stTimeGrab.wMonth,ii->stTimeGrab.wDay,ii->stTimeGrab.wHour);
	SHCreateDirectoryEx(0,dir,0);

	swprintf_s(file,MAX_PATH,L"%s-%04d%02d%02d%02d%02d%02d-%s-%d-%03d-%d-%d-%04d-00-%d",
		ii->szDevCode,ii->stTimeGrab.wYear,ii->stTimeGrab.wMonth,ii->stTimeGrab.wDay,
		ii->stTimeGrab.wHour,ii->stTimeGrab.wMinute,ii->stTimeGrab.wSecond,ii->szPlateNo,
		ii->wPlateColorCode,ii->ui16Speed,ii->wDirCode,ii->ui8Lane,ii->wVioCode,
		ii->ui8ImgIndex);
	swprintf_s(fmted,fmtlen,L"%s\\%s",dir,file);
	//
	TCHAR jpg[MAX_PATH]={0};
	_tcscpy_s(jpg,fmted);
	_tcscat_s(jpg,_T(".jpg"));

	TCHAR xml[MAX_PATH]={0};
	_tcscpy_s(xml,fmted);
	_tcscat_s(xml,_T(".xml"));

	//保存jpg
	Save2File(jpg,(unsigned char*)img,img_len);
	//localfile
	wcscpy_s(ii->szLocalFile,jpg);
	//remotefile
	swprintf_s(ii->szRemoteFile,L"%s(%s)\\%s-%04d%02d\\%02d\\%s.jpg",
		ii->szDevName,ii->szDevIP,ii->szDevCode,ii->stTimeGrab.wYear,ii->stTimeGrab.wMonth,
		ii->stTimeGrab.wDay,file);
	//保存XML
	SaveToXml(ii,xml);
	//设置文件名
	ii->ui8UseDeviceFilename = 1;
	_tcscpy_s(ii->szFileName,jpg);
	//追加XML
	AppendInfToImg(ii,jpg);
	return 0;
}
int _format_(LPCTSTR file,void*img,int img_len)
{
	return 0;
}
unsigned int version()
{
	return MAKEWORD(0,1);
}
wchar_t* description()
{
	return L"1002-yyyymmddhhmmss-plate-ClrCode-Speed-37-Lane-vio-00-imgidx";
}

其他插件类似的实现接口函数,而后把这些插件 dll 放入主程序下的某个目录,一般目录名为 plugins 。在主程序中遍历这个目录中的 dll 文件,针对每一个遍历到的文件,利用 GetProcAddr() 获得接口函数指针,如果获取成功,则认为是正确实现的合法 dll,在合理的逻辑中调用这些接口实现。

五,热更换和热升级

    指在不重启软件的情况下更换插件,可以通过卸载正在运行的插件 dll,然后删除之,再更换,再加载的流程来实现。但这往往是没有必要的,因为在卸载 dll 的时候代码逻辑已经停止(即和软件退出没什么两样)。

    如果先把需要更新的插件先放入插件目录,加载进去运行,再卸载旧的 dll,则会不一样----这个更换过程是不易觉察的。