论坛首页 编程语言技术论坛

C++ Plug-in 技术的一些深度思考(1)

浏览 1862 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-12-02   最后修改:2009-12-03

C++ Plug-in 技术的一些深度思考(2)

 

 

最近,忙于思考如何重构一个Unix系统。这个系统是由C++写的,重构的思路是希望能够抽取出一些公共的东西,作为Core。另外一些东西做成Plug-in。这样以后如果客户的规范更新了,我们只需要增加修改Plug-in就好了,Core的部分不需要再修改。

 

      要满足这个要求,有些基本的原则:

      [1] Core里面所有的东西不能依赖Plug-in里面。这种依赖可以理解为Build(Compiler + Link)Core里面模块的时候,不需要用到Plug-in里面的头文件或者object文件;

      [2] 部署的时候,可以不用重新部署Core。只需要Update Plug-in,就可以获得新功能。

      [3] 现有代码最好能够最大程度的复用。 

       因为这些要求,对C++设计编码在大型系统中的使用,有了一些新的体会和认识。为了简化问题,我抽象了一个简单的例子。

 

       有这样一个需求: 给定一个整数数组,我们需要对这个数组作些运算,这个运算现在看到的有求和,求积,未来可以增加运算规则,譬如求最大值,最小值,平均值等等。现在不能预测未来需要那些运算,系统需要扩展的能力。

 

       首先来看看运用面向对象和工厂模式给出的初始设计。

       我们有一个ICalc的借口,接口里唯一一个需要Override的函数是:  virtual void calc(int *p, size_t size, int *result) = 0  这里说唯一是从设计角度考虑的,因为它本身的析构函数也是纯虚函数。另一方面,这个函数没有要求是const的,这个不会影响我们的讨论。因为现有系统中并没有一致的使用const,所以我们设计这个接口的时候,没有把const考虑进去。

         接下来,我们设计两个实体类CalcAdd和CalcMul,当然它们都Override了calc这个方法。我们又设计了一个CalcFactory,它有个静态函数getCalc(string calcType)。通过calcType的值,返回正确的Calc类型。



目录结构 

 

// include/ICalc.h
#ifndef _ICALC_H_
#define _ICALC_H_

#include <stdlib.h>

class ICalc
{
    public:
        virtual void calc(int *start, size_t size, int *result) = 0;
        virtual ~ICalc() = 0;
};

#endif //_ICALC_H_

 

//include/CalcFactory.h
#ifndef _CALCFACTORY_H_
#define _CALCFACTORY_H_

#include <string>

#include "ICalc.h"

class CalcFactory
{
    public:
        static ICalc* getCalc(std::string calcType);
};

#endif //_CALCFACTORY_H_

 

  

//include/CalcAdd.h
#ifndef _CALCADD_H_
#define _CALCADD_H_

#include "ICalc.h"

class CalcAdd : public ICalc
{
    public:
        virtual void calc(int *start, size_t size, int *result);
        virtual ~CalcAdd();
};


#endif //_CALCADD_H_

        

    给纯续函数提供函数体是可以的,在这种情况下也是必需的。

#include "../include/ICalc.h"

ICalc::~ICalc(){ }

   

   

// src/CalcFactory.cpp
#include "../include/CalcFactory.h"
#include "../include/CalcAdd.h"
#include "../include/CalcMul.h"

using std::string;

ICalc* CalcFactory::getCalc(string calcType)
{
  if ("Add" == calcType)
  {
    return new CalcAdd;
  }
  else if ("Mul" == calcType)
  {
    return new CalcMul;
  }
  else
  {
    return 0;
  }
}

  

 

// src/CalcAdd.cpp
#include "../include/CalcAdd.h"

void CalcAdd::calc(int *p, size_t size, int *result)
{
    for (size_t i = 0 ; i < size; ++i)
    {
        (*result) += p[i];
    }
}

CalcAdd::~CalcAdd() { }

 

// src/main.cpp
#include <iostream>
#include <string>

#include "../include/CalcFactory.h"
#include "../include/ICalc.h"

using namespace std;

int arr[] = { 1, 2, 3, 4, 5, 6 };
int result; // result = 0

int main(int argc,char* argv[])
{
    if (argc < 2)
    {
        cerr << "Usage: calc Add|Mul" << endl;
        return -1;
    }
    if (string("Add") == argv[1])
    {
        result = 0;
    }
    else if (string("Mul") == argv[1])
    {
        result = 1;
    }
    ICalc *calc = CalcFactory::getCalc(argv[1]);
    if (!calc)
    {
      cerr << "Fail to calc." << endl;
      return -2;
    }
    calc->calc(arr, sizeof(arr)/sizeof(int), &result);
    cout << "result = " << result << endl;
    delete calc;
    return 0;
}

    

 

               大致的程序如上述,上面没有给出CalcMul.h和CalcMul.cpp。因为这个和CalcAdd完全一致。大家可以下载v1.tar.bz2看最初设计的源代码。大家可以使用make -f Makefile.(mac|sun|lnx) all Build整个系统,最后会在bin下面生成一个可执行文件calc,运行就可以看到结果。

          可以看到,这个版本其实是个不错设计的。因为如果我们要增加一个求最小值的算法,只需要设计一个CalcMin,实现ICalc接口,重新修改CalcFactory的getCalc函数,增加一个“Min”的分支,就OK了。这个设计也符合开闭原则。就是对修改开放,对整个工作流的修改是关闭的。可是,这样的设计还是有几个问题:

[1] CalcFactory.obj依赖于CalcAdd.h,CalcMul.h。如果增加一个新的算法,这个文件必须重新修改,编译,链接。

[2] calc虽然不依赖于CalcAdd.h,CalcMul.h。换言之,如果CalcAdd修改了,calc不需要重新编译,但是却需要重新链接。因为calc需要CalcAdd.o, CalcMul.o。

基于这两点,我们没有办法把CalcAdd这样的算法做成Plug-in。因为更新Plug-in,我的Core程序也需要更新。很多时候这不是一个严重的问题,但是有时候我们确实需要简单的更新Plug-in。因为如果只更新Plug-in,我们可以减少很多Core程序测试的时间和工作量。这个在大型程序带来的价值是巨大的。于是,我们继续改进我们的设计。

 

C++ Plug-in 技术的一些深度思考(2)

  • 大小: 14.4 KB
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics