这里介绍一个办法来实现基于MFC应用程序的模块化,至于为什么要模块化,模块化有哪些优点哪些缺点这里不做讨论。
假设有这样一个工程,有主程序Main.exe和两个DLL模块Module1.dll以及Module2.dll组成,所有的模块都动态链接到MFC动态库(静态链接到MFC动态库的情况会有些区别)。
一、首先要建立先定义好一组接口,用来实现主程序和DLL模块双向交换。
1、主程序Main.exe要提供一个接口IApp给DLL模块,使得DLL模块可以使用主程序的一些功能,比如写日志等。
class IApp
{
public:
virtual ILog* GetLog()= 0; //返回日志接口指针
//这里可以扩展其他的虚函数,但是新的虚函数定义一定要放在原有接口的最后面,这和虚函数表有关。
//这样就达到了可扩展性。
};
2、模块Module1.dll以及Module2.dll也要分别提供一个或多个接口给主程序Main.exe调用。
分别为:
class IModule1
{
public:
virtual void SetApp(IApp* lpApp) =0; //把主程序的接口传给DLL
virtual void DoSomething()= 0;//做一些处理
//这里一样支持可扩展
};
class IModule2
{
public:
virtual void SetApp(IApp* lpApp) =0; //把主程序的接口传给DLL
virtual void ShowDlg()= 0;//显示对话框
//这里一样支持可扩展
};
二、建立各自的MFC工程并实现相应的接口
1、建立主程序MFC工程Main.exe,动态链接到MFC动态库,然后实现IApp,ILog等接口。
2、建立MFC工程Module1.dll,可以是规则DLL,也可以是非规则DLL,但是要求动态链接到MFC动态库。
规则DLL与非规则DLL在资源使用,窗口管理等方面有些区别,一般建议使用非规则DLL。
Module1.dll不直接导出类,而是导出一些函数来创建对应的接口,比如导出函数CreateModule1Instance()用来创建IModule1实例,以此类推。这是为了把创建实例的代码放在DLL中,也是为了支持将来升级扩展。因为后续版本的Module1.dll所实例化的对象占用的内存大小可能不一样的,如果把实例化代码放在Exe中,则需要重新编译Exe。
3、Module2.dll实现同上。
三、扩展升级
1、不修改接口的扩展
比如要修改IModule1中DoSomething函数所实现的功能,那就只需要修改并升级Module1.dll即可。
2、需要修改接口的扩展
修改IModule2接口(有可能也需要修改IApp接口):
class IModule2
{
public:
virtual void SetApp(IApp* lpApp) =0;
virtual void ShowDlg()= 0;
virtual void DoSomething()= 0;//增加的函数
};
这样需要修改Module2.dll和主程序Main.exe的代码来实现新功能。但是修改后Module2.dll一样支持老版本的Main.exe,因此也就只需要维护一个版本的Module2.dll即可。
四、一些应用场合
1、 不用的客户使用不同的模块,从而实现业务逻辑的不同。(比如使用不同的数据库。)
2、 主程序需求相对稳定,但是某个模块功能经常需要升级。(需求特别紧急的情况下,其好处更是明显,目前笔者的产品就是这样的。)