zoukankan      html  css  js  c++  java
  • 一种松耦合的分层插件系统的设计和实现

    C++编写的桌面软件中传统的App+Dll的架构已经显现出的模块之间的强耦合、维护性差、升级不方便等诸多弊端,为此我进行了一些思考,有兴趣的朋友请看我以前的一个思考片断:《Windows平台下C++插件系统实现的几个关键技术问题及其解决思路》,今天则具体介绍的我的一个具体设计和实现。

     

            我的目标是要去除功能模块的相互依赖,在模块调用必须采用动态加载的办法,但同时各个模块可以进行自由地进行通讯。我的设想大致是这样的:在动态加载各个模块后调用统一接口后生成插件对象,在某个插件对象都能通过标识符找到其它的插件对象,通过统一的接口将数据传给它们。系统架构图如下:

    系统流程图如下:

     

               大家可以看到,要构建这样的系统需要解决两个关键问题:

     

    1. 如何动态加载dll创建插件对象以及插件基类对象的接口设计

     

    2. 插件模块之间的通讯问题

     

             如何解决这两个问题我已有了基本的思路。为了验证我的思路是可行的,我用VC2008新建了一个LayeredArchit解决方案,该解决方案实现这样一个简单功能:统计指定目录下的文件数,然后将统计结果保存到一个文本文件。这个解决方案由以下工程组成:

     

    BaseObjLib   ——   底层库,用于定义底层对象和接口

     

    StatFile    ——  统计文件插件

     

    OutputFile ——  将统计结果输出到文本文件的插件

     

     CmdApp   ——  一个主调用程序(是一个控制台程序)。

     

     

             在每个插件模块都实现一个插件对象,继承基类插件对象IPluginObj,里都有这样一个统一的插件导出接口来生成插件对象:

    1. extern "C" __declspecdllexport )  IPluginObj* __stdcall GetPluginObj(const std::string &strID)  

                在动态加载插件库之后就调用这个接口。这样就解决了动态创建插件对象的问题。


               接下来我们解决插件之间的数据通讯问题。前面的设计已经实现了在任何插件模块里都能找到其它的插件对象。一个很自然的设计是在基类定义这样一个接口:

    1. virtual BOOL ProcessData(void* pData){ return FALSE;}  

     

          然后在派生插件对象里重载这个接口。这个设计基本可以实现数据通讯,但是有一个严重弊端是void*是类型不安全的,假如数据接收方将其类型转换错了程序就很可能崩溃。特别是当一个插件需要接受两种类型数据时。因此必须解决类型安全问题。


            之后我想到了利用微软的com模型的万能类型VARIANT,但是看了一篇文章《用户自定义结构数据与VARIANT转换》就放弃了,感觉转换太繁琐了。后面惊喜地发现原来boost库早已实现了一个万能类型boost::any。boost::any真的很符合我的需求。具体是在基类插件对象类定义一个虚接口:


    1. virtual BOOL ProcessData(boost::any& anyData){ return FALSE;}  

           在主程序里找到统计插件对象,并将路径传递给它:

    1. IPluginObjPtr ptrStatFile = pPluginFactory->GetPlugin(_T("StatFile.dll"));  
    2. std::string strFolder = _T("D:\dev-utility-tools\doc");  
    3. boost::any anyString = strFolder;  
    4. ptrStatFile->ProcessData(anyString);  

    // 统计模块的ProcessData函数的实现

    1. BOOL CStatFilePlugin::ProcessData( boost::any& anyData )  
    2. {  
    3.     if(anyData.type() == typeid(std::string))    
    4.     {  
    5.         std::string strPath  = boost::any_cast<std::string>(anyData);   
    6.         if(::GetFileAttributes(strPath.c_str())==-1)  
    7.         {  
    8.             std::cout<<"目录不存在"<<std::endl;  
    9.             return FALSE;  
    10.         }  
    11.         m_info.m_strPath = strPath;  
    12.         std::cout<<"现在正在统计的是:"<<m_info.m_strPath<<std::endl;  
    13.         std::cout<<"请等待!!!"<<std::endl;  
    14.           
    15.         std::string strTmpDir = m_info.m_strPath + TEXT("\");  
    16.         // 统计文件数  
    17.         StatAllFileInFolder(strTmpDir);  
    18.   
    19.         std::cout<<"文件夹数:"<<m_info.m_FolderNum<<std::endl;  
    20.         std::cout<<"文件数:"<<m_info.m_FileNum<<std::endl;  
    21.   
    22. // 找到输出到文件插件对象,将统计结果传给它  
    23.         CPluginFactory* pPluginFactory = CPluginFactory::Instance();  
    24.         IPluginObjPtr ptrOuttoFile = pPluginFactory->GetPlugin(_T("OutputFile.dll"));  
    25.         boost::any anyInfo = m_info;  
    26.         ptrOuttoFile->ProcessData(anyInfo);  
    27.   
    28.         return TRUE;  
    29.     }  
    30.     return FALSE;  
    31. }  

            这样就安全地实现了插件间的数据通讯。需要指出的是需要通讯的插件间不相互依赖,但需要都包含的定义了传递数据结构体的头文件。具体请见源码下的DataExchange文件夹,该文件夹就是定义需要通讯的数据结构体。


    程序的效果图如下:


             一个扩展设想是:消息本质上也是一种数据。消息的传递也可以参考数据的传递来实现。

     

            源码已上传至http://code.google.com/p/dev-utility-tools/source(具体见PluginArchit解决方案),采用GPL V2.0开源协议,有兴趣请下载看看。

     

    参考文献:

     

    1.     4.6.1 万能类型boost::any

    from:http://blog.csdn.net/clever101/article/details/8656720

  • 相关阅读:
    牛客IOI周赛17-提高组 卷积 生成函数 多项式求逆 数列通项公式
    6.3 省选模拟赛 Decompose 动态dp 树链剖分 set
    AtCoder Grand Contest 044 A Pay to Win 贪心
    5.29 省选模拟赛 树的染色 dp 最优性优化
    luogu P6097 子集卷积 FST FWT
    CF724C Ray Tracing 扩展欧几里得 平面展开
    5.30 省选模拟赛 方格操作 扫描线 特殊性质
    5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集
    Spring main方法中怎么调用Dao层和Service层的方法
    Bug -- WebService报错(两个类具有相同的 XML 类型名称 "{http://webService.com/}getPriceResponse"。请使用 @XmlType.name 和 @XmlType.namespace 为类分配不同的名称。)
  • 原文地址:https://www.cnblogs.com/lidabo/p/3435016.html
Copyright © 2011-2022 走看看