1、应用接口定义
没有基类,没有显式的接口ID,全部由纯虚函数组成,例如:
interface Ix_MyObj
{
virtual void foo() = 0;
};
2、实现接口
接口实现类(也叫组件类):从接口派生,不需要特殊基类,没有显式的组件类ID,例如:
#include "Ix_MyObj.h"
class Cx_MyObj : public Ix_MyObj
{
protected:
Cx_MyObj();
virtual ~Cx_MyObj();
virtual void foo();
};
为了针对接口编程、让使用者不依赖于具体实现类,该类应当不能直接实例化,所以可以将构造函数设置为保护成员。
一个实现类可以实现多个接口,其方法是从多个接口派生,然后实现接口的函数。
实现类可以继承,这样就允许多个类复用相同的实现部分。
3、组件类ID的形式
要实例化一个组件类,需要知道其组件类ID。通常组件类ID有三种形式:
1)在类定义中使用__declspec(uuid())关键字指定,使用__uuidof引用该组件类ID,该方法在COM中广泛使用;
2)直接使用GUID字符串定义一个常量名,需要用到组件类ID时使用该常量名,不依赖于编译环境;
3)使用DWORD值定义唯一的模块ID,组件类ID在模块ID的基础上进行1到255的偏移,该方法在飞腾创艺产品中使用。
考虑到尽可能通用、编译环境的uuid复杂性,选择了第二种方法。
专门在一个H文件中定义可供外部实例化的组件类的ID,这样使用者不需要知道该类如何实现就能使用该组件类ID,例如:
const LPCWSTR ClsID_MyObj = L"80313e22-597a-4216-a282-b8ed85722c9c";
const LPCWSTR ClsID_OtherObj = L"d57720ee-f816-4f40-a47b-44ea47b15321";
4、公共接口 Ix_Object
所有接口都可转换到Ix_Object接口,该接口使用引用技术机制管理对象生命周期。
Ix_Object的继承关系如下图所示:
interface Ix_Object
{
virtual void AddRef() = 0;
virtual void Release() = 0;
};
在模板类Cx_Object中实现了Ix_Object接口,实现了对象创建函数(静态成员)CreateObject。
5、单实例对象
用模板类Cx_SingletonObject标记单实例类,Cx_SingletonObject在第一次创建对象时将记下该对象,下次再创建时直接返回该对象的引用,同时将该对象添加到全局链表中,以便模块卸载时能销毁该对象。
Cx_ModuleItem类负责管理一个模块内的单实例对象,采用了链表技术。其ClearModuleItems函数在插件模块卸载时调用。
6、标记一个模块有哪些可实例化的组件类
在一个模块中标记出有哪些组件类可实例化、组件类对应的类ID、组件类创建类型(普通类、单实例类、支持特殊接口的单实例类等),在一个CPP文件中统一指定这些信息便于管理,避免分散在各个CPP文件中。这些信息记录到一个_XCLASSMETA_ENTRY 静态数组中,数组元素包含组件类ID、类名、创建对象的模板类等信息。
_XCLASSMETA_ENTRY 的 clsid 为组件类ID(如ClsID_MyObj),pfnCreator 为对象创建函数的地址(如&Cx_Object<Cx_MyObj>::CreateObject)。该模块的所有可实例化组件类的信息都放到 _XCLASSMETA_ENTRY::s_classes 数组中。
struct _XCLASSMETA_ENTRY
{
LPCWSTR clsid;
PFNXObjectCreator pfnCreator;
static const _XCLASSMETA_ENTRY s_classes[];
};
下面是一个模块的组件类数组信息的例子:
const _XCLASSMETA_ENTRY _XCLASSMETA_ENTRY::s_classes[] = {
{ ClsID_MyObj, &Cx_Object<Cx_MyObj>::CreateObject },
{ L"", NULL }
};
为了方便于使用,利用宏来简化上面的代码,简化后的结果为:
XBEGIN_DEFINE_MODULE()
XDEFINE_CLASSMAP_ENTRY(ClsID_MyObj, Cx_MyObj)
XEND_DEFINE_MODULE()
需要下列宏:
XBEGIN_DEFINE_MODULE()
XDEFINE_CLASSMAP_ENTRY(clsid, cls)
XDEFINE_CLASSMAP_ENTRY_Singleton(clsid, cls)
XDEFINE_SPECIAL_INTERFACE_ENTRY_Singleton(clsid, iid, cls)
XEND_DEFINE_MODULE()
7、创建对象的原理
使用 xCreateInstance 函数创建对象,创建一个对象的内部过程为:根据 clsid 在 _XCLASSMETA_ENTRY::s_classes 中查找对象创建函数地址,创建出一个对象,得到 Ix_Object 指针,然后动态转换为特定接口。
对于扩越多个插件的对象创建,是由插件管理器来进行的。由插件管理器将所有插件的 clsid 对应的对象创建函数地址管理起来,当在一个插件内部无法创建对象时,进入插件管理器继续创建对象。
使用智能指针类Cx_Interface和Cx_Ptr来进行对象创建、对象生命期管理。
8、插件管理器原理
将所有插件管理起来,使得插件模块之间可以相互使用接口。具体内容和以前版本相似,省略。