1、第四章 COM特性,前一些章节已经讲述了COM的基本规范以及COM实现的细节。现在就可以编写真正的组件了。 可以把大的系统分解成一些小的组件对象,每个组件对象按照COM规范来实现,再编写一个或多个客户程序调用这些组件,组件之间或组件与客户之间通过COM接口进行通信。简单的程序这样做就可以了。,4.1 可重用性:包容和聚合,面向对象系统的三个最基本的特性分别是:封装、多态、重用 封装性体现在对所有对象状态信息的访问只能通过接口来访问。 多态性主要体现在三个层次上:接口成员函数、单个接口、一组接口 所谓重用是指:当一个程序单元能够对其它的程序单元提供服务时,尽可能地重用原先单元程序地代码,既可以在
2、源代码一级重用,也可以在可执行代码一级重用。C+语言地重用性位于源代码一级;而COM是建立在二进制一级上地标准,因此重用性也必然建立在二进制一级。,4.1.1 包容和聚合,包容和聚合是COM地两种重用模型。 包容:假如已经实现了一个COM对象A,他是现了接口ISomeInterface,在此还要实现另一个COM对象B,这个COM对象既要实现接口ISomeInterface,也要实现接口IOtherInterface,而且, ISomeInterface接口所提供地服务与对象A的接口所提供的服务基本一致,这样就可以重用对象A来实现对象B的接口功能。最简单的办法是:在实现对象B的接口ISomeIn
3、terface时调用对象A的相应成员函数,对于对象A来说,它只是当作一个普通的COM对象,而对于对象B来说,虽然它本身是一个COM对象,但同时也是对象A的客户,因为他调用对象A的功能服务。对于对象B的客户来说,他根本就不知道对象A的存在。包容模型在实际使用过程中可以非常灵活,对象B的成员函数在调用对象A的接口成员之前或者调用返回之后也可以进行其它一些操作,因此,对象B的ISomeInterface接口提供的功能可以超出对象A的接口功能,返回结果也可以不一致。,包容和聚合,对象A和对象B可以只是一个服务器与客户的关系。 对象A的创建和释放完全在对象B内部进行。对象B构造时,同时创建对象A的接口指
4、针,以便自阿对象B的成员函数中使用;当对象B被释放时,它先释放对象A,这样可以完成对象B对对象A的篏套使用,形成包容。要对其优化,便可在对象B需要时创建对象A,在不需要时释放对象A。 聚合:对象B要实现两个接口ISomeInterface和IOtherInterface,而对象A实现了ISomeInterface接口,并且不要对此接口进行修改便可满足对象B的要求。如果采用包容模型,那么对象B要实现两个接口,并且在ISomeInterface接口的成员函数中调用对象A的相应函数,采用聚合则不一样。对象B本生不实现接口ISomeInterface,他只实现IOtherInterface接口,但它也
5、能提供接口ISomeInterface的功能,当对象B的客户请求接口ISomeInterface时,对象B把对象A的ISomeInterface接口暴露给客户,因此,客户调用ISomeInterface是直接与对象A进行交互的,而客户知道的只是对象B。,包容和聚合,在交互模型中,被聚合的对象A虽然直接向对象B的客户提供功能服务,但它的生存周期受对象B控制,而且其它的一些行为也受到对象B控制,包括内部状态初始化、获取数据等。 实现交互的关键在于对象B的QueryInterface成员函数,当客户向对象B请求ISomeInterface接口时,对象B的QueryInterface函数把对象A的IS
6、omeInterface接口指针放到输出参数中,客户程序就获得了对象A的ISomeInterface接口指针,可以直接调用接口成员函数了。 这样还是有问题的,问题在于:当客户程序通过ISomeInterface:QueryInterface函数又请求其它的接口指针时,问题就出现了。因为对象A并不知道对象B实现可什么样的接口,而根据COM规范,客户程序从对象B的任何一个接口可以获取其它的任何接口指针。而且,在正常情况下,客户程序调用对象A的ISomeInterface:QueryInterface函数请求IUnknown接口指针获取的是对象A的Innknown接口指针,而客户程序调用对象B的IO
7、therInterface:QueryInterface函数请求IUknown接口指针获取的是对象B的IUknown接口指针,而COM规范要求的IUknown接口指针必须唯一。因此,为了使聚合顺利实现,对象A也必须能够适应在被聚合的情况下进行的特殊处理,尤其是接口的QueryInterface成员函数,在被聚合的情况下,当客户请求它所不支持的接口或请求Ikknown接口时,它必须把控制交给外部对象,有外部对象决定客户程序的请求结果。,包容和聚合,选择重用模型依据的原则如下:在一个组件对象在行为上更类似于另一个组件对象的客户,并且它要调用第二个对象的某些对象接口的情况下,适合用包容模型。如果一个
8、现成的组件对象所实现的接口与将要实现的对象的接口的行为完全一致,则采用聚合模型更为适合,当然前提条件是现有的组件对象必须支持聚合特性才行。 包容和聚合是COM对象的两种出模型,它们相互并不矛盾,因此可以在一个对象中同时使用两种模型,有的接口通过包容实现,有的接口通过聚合实现。,包容实现,内部对象A实现了接口ISomeInteface,外部对象B要实现接口ISomeInterface和IOtherInterface,其接口如下定义:class ISomeInterface:public Iuknownpublic:virtual HRESULT_stdcall SomeFunction()=0;
9、class IOtherInterface:public Iuknownpublic:virtual HRESULT_stdcall OtherFunction()=0;在客户程序、组件对象A和组件对象B三者之中,B包容A,客户程序只知道对象B的存在,实际上对象B是对象A的客户,负责对象A的创建、调用和释放等工作。,包容实现,列出其C+定义如下:class CB:public ISomeInterface,public IOtherInterfaceprotected:Ulong m_ref;public:CB();CB();/成员函数HRESULE Init();private:ISomeI
10、nterface * m_pSomeInteface;因为对象B包容对象A,所以在对象B的成员函数中要调用对象A的接口成员函数,因此在对象B的定义中加了数据成员m_pSomeInteface,记录对象A的接口指针。,包容实现,类CB的实现:CB:CB()m_pSomeInteface=NULL;m_ref=0;CB:CB()if(m_pSomeInteface=NULL)m_pSomeInteface-Release();HRESULT CB:Init()HRESULT result=:CoCreateInstance(CLSID_ComponentA,Null,CLSCTX_INPROC_S
11、ERVER,IID_ISomeInterface,(void * *) ,包容实现,需要对其进行说明如下:(1) 在Init成员函数中,对象B创建了包容对象A,只要创建成功,则数据成员m_pSomeInteface记录了对象A的ISomeInteface接口指针;当对象B被析构时,通过调用对象A的Release成员释放对象A。(2) 在对象B实现接口ISomeInteface的成员SomeFunction函数时,只是调用了对象A的相应函数,出的概念在此体现出来。可以在对象初始化构造时调用Init成员函数,因此对对象B的类厂的CreateInstance成员函数的实现有所不同,其如下:HRESU
12、LT CBFactory:CreateInstance()pObj=new CB();if(pObj=NULL)hr=pObj-Init();if()hr=pObj-QueryInterface(iid,ppv);return hr;,包容实现,当要对接口进行扩展时,可以通过接口继承来实现接口的扩展,在实现新的接口时可以一个包容来重用以前的组件。例如,定义新的接口ISomeInterfaceEX对接口ISomeInterface进行扩展,其定义如下:class ISomeInterfaceEX:public ISomeInterfacepublic:virtual HRESULE _stdca
13、ll NewFunction()=0;ISomeInterfaceEX接口可以重用已经实现的ISomeInterface接口。实际上新的对象只要实现新加的成员函数NewFunction即可,而SomeFunction成员函数可以直接调用m_pSomeInterface的成员实现。如果新的对象的类名仍为CB,则代码如下:virtual HRESULE _stdcall CB:SomeFunction()return m_pSomeInterface- SomeFunction();,包容实现,在包容模型中,外部对象重用内部对象的接口时,可以是简单的调用关系,也可以在调用内部对象接口成员函数之前或之后加上其它的处理,甚至在某些情况下可以加上一些条件处理,比如下面的情况是有可能的:HRESULT _stdcall CB:SomeFunction()if()elseHRESULT result=m_pSomeInterface-SomeFunction();return result;总之,包容模型的实质是客户组件的关系,在实现外部对象时可以灵活使用。,4.1.3 聚合实现,