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
分享到:
- 2009-12-02 14:14
- 浏览 2496
- 评论(0)
- 论坛回复 / 浏览 (0 / 1864)
- 查看更多
相关推荐
IDA_plug-in_writing_in_C_or_C++, 中文和英文两个。IDA_plug-in_writing_in_C_or_C++
《GNU ARM Eclipse Plug-in:为Eclipse开发环境增添ARM支持》 GNU ARM Eclipse Plug-in是专为Eclipse集成开发环境(IDE)设计的一款扩展插件,由开发者ilg-ul和justxi共同贡献。该插件的主要目的是为了在Eclipse...
Bridging the gap from theory to programming, Designing Software Synthesizer Plug-Ins in C++ For RackAFX, VST3 and Audio Units contains complete code for designing and implementing software ...
开发者使用Visual C++编写的应用程序,通常会依赖于一些特定的库文件,这些库文件在用户计算机上可能并未预装。因此,Visual C++ Redistributable就应运而生,它的主要功能就是提供这些必要的运行时库,使得用户无需...
1. "Microsoft Visual C++ 2015-2019 Redistributable - 14.26.28720.3 x64.exe" 是64位版本的安装程序,提供版本号为14.26.28720.3的运行库,适用于64位Windows系统。 2. "Microsoft Visual C++ 2015-2019 ...
warning: gcc-c++-4.4.7-3.el6.x86_64.rpm: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY error: Failed dependencies: gcc = 4.4.7-3.el6 is needed by gcc-c++-4.4.7-3.el6.x86_64 libstdc++ = 4.4.7-...
C++编程思想-Thinking in C++
gcc-c++-3.4.6-3.1.x86_64.rpm
一个典型的插件系统由两部分组成:主应用程序(Host Application)和插件(Plug-In)。主应用程序提供了一个接口,允许插件与其交互。而插件则实现了这些接口,提供额外的功能。为了使插件能够与主应用程序通信,...
基于libmodbus开源库 C++ modbus-rtu通信测试程序源码,vs2013平台。此文件为主站程序可实现与从站(从站可以使用Modbus Slave 仿真软件)通信,实现寄存器的读写功能。
- **创办目的**:基于上述背景,《C++View》创刊旨在填补这一空缺,提供一个专注于C++基础知识和技术深度讨论的平台。 #### 2. C++View 内容概览 - **走近C++之父Bjarne Stroustrup**:通过专访C++创始人之一Bjarne...
《C++ Templates - The Complete Guide》是一本深入探讨C++模板技术的专业书籍,它为开发者提供了全面、详尽的模板知识。模板是C++语言中一个强大的特性,它允许我们编写泛型代码,提高代码的复用性和灵活性。这本书...
-Giving guidance on how to use Templates in Modern C++ -For programmers, who just use templates, who provide class and function templates, and who provide generic and foundation libraries -Covering ...
以下是一些关键步骤: 1. **创建串口对象**:首先,我们需要在程序中实例化一个`CSerialPort`对象,通常在类的构造函数中完成。 ```cpp CSerialPort m_serialPort; ``` 2. **配置串口参数**:接着,使用`...
Object-Oriented Programming in C++Object-Oriented Programming in C++Object-Oriented Programming in C++
compat-gcc-c++-7.3-2.96.128.i386.rpm
gcc-c++-4.4.7-4.el6.i686.rpm cpp-4.4.7-4.el6.i686.rpm gcc-4.4.7-4.el6.i686 .rpm libstdc++-devel-4.4.7-4.el6.i686.rpm libstdc++-4.4.7-4.el6.i686.rpm
gcc-c++-4.4.4-13.el6.x86_64.rpm
compat-gcc-c++-7.3-2.96.122.i386.rpm
【标题】"基于树莓派Qt+opencv+yolov5-Lite+C++部署深度学习推理"涉及的关键技术点包括树莓派开发、Qt图形界面框架、OpenCV图像处理库、C++编程语言以及轻量级的YOLOv5深度学习模型。 首先,树莓派是一种低成本、...