浅析COM的思想及原理

来源:互联网 发布:庞珣 知乎 编辑:程序博客网 时间:2024/05/21 06:44
 

[COM/ATL]浅析COM的思想及原理

分类: 那些杂项 357人阅读 评论(0) 收藏 举报

目录(?)[+]

COM--Component Object Model,即组件对象模型,它是微软提出的一套开发软件的方法与规范。它也代表了一种软件开发思想,那就是面向组件编程的思想。

COM编程思想--面向组件编程思想(COP)

  众所周知,由CC++,实现了由面向过程编程到面向对象编程的过渡。而COM的出现,又引出了面向组件的思想。其实,面向组件思想是面向对象思想的一种延伸和扩展。因此,就让我们先来回忆一下面向对象的思想吧。

  面向对象思想是将所有的操作以及所操作的对象都进行归类(class实现),而它的目标是要尽量提高代码的可重用性(这也是面向对象相比面向过程最大的优点之一)。比如,有两个程序AB都需要对class C的对象进行操作,那么class C的代码就可以重用了(AB都可以使用class C的代码)。但是,对于这一点,面向对象做得并不够好。还是举刚才的例子,程序AB都要对class C的对象进行操作,那么,程序AB的编程人员都必须将class C的代码拷贝过来,然后重新编译一次,这将是多么麻烦的事!况且,如果class C的代码没有公开,那这种重用就根本不可能实现了(除非程序AB的编程人员和class C的编程人员是同一个人或者团队,但这样局限性就相当大了)

  由于面向对象的这些局限性,很多程序员就会想,如果我们编程需要重用别人的成果时,不需要重新编译别人的代码那就好了。换句话说,我们要达到的目标是,直接重用别人的成果而不是重用别人的代码。这样说也许很抽象,举个例子大家就会比较明白。比如将class C的代码编译生成一个dll,那么当其他程序员想要重用class C时,就只需要在自己的程序中加载这个dll而不需要重新编译class C的代码了(这也就是组件必须要能动态链接的原因)。正是这种思路引出了面向组件的编程思想。

  下面,我就简单介绍一下面向组件的思想。在以前,应用程序总是被编写成一个单独的模块,就是说一个应用程序就是一个单独的二进制文件。后来在引入了面向组件的编程思想后,原本单个的应用程序文件被分隔成多个模块来分别编写,每个模块具有一定的独立性,也应具有一定的与本应用程序的无关性。一般来说,这种模块的划分是以功能作为标准的。比如,一个网上办公管理系统,从功能上说它需要包含网络通信、数据库操作等部分,我们就可以将网络通信和数据库操作的部分分别提出来做成两个独立的模块。那么,原本单个的应用程序就分隔成了三个模块:主控模块、通信模块和数据库模块。而这里的通信模块和数据库模块还可以做得使其具有一定的通用性,那么其他的应用程序也就可以利用这些模块了。这样做的好处有很多,比如当对软件进行升级的时候,只要对需要改动的模块进行升级,然后用重新生成的一个新模块来替换掉原来的旧模块(但必须保持接口不变),而其他的模块可以完全保持不变。这样,软件升级就变得更加方便,工作量也更小。

  说了这么多,总结一下:面向组件编程思想就是:模块分隔。这里的分隔有两层含义,第一就是要,也就是要将应用程序(尤其是大型软件)按功能划分成多个模块;第二就是要,也就是每一个模块要有相当程度的独立性,要尽量与其他模块开。这四个字是面向组件编程思想的精华所在,也是COM的精华所在!理解了这四个字,也就真正理解了面向组件编程的思想。(这里说一点题外话,COM其实是一套规范或者说一套标准,但是在我看来,COM的核心还在于它的思想,也就是面向组件编程思想。标准谁都能定,但是思想只有一个!)

COM的优点

  COM的优点也就是面向组件编程思想的优点。而面向组件编程思想有很多的优点,上面所说的便于软件升级只是其中之一。对于它的优点,我总结了一下,有下面几条:

  1. 便于重用,使软件开发更快捷
  2. 便于软件升级
  3. 便于软件开发的分工协作
  4. 便于用户定制自己的应用

  以上几点,第一和第二点都不用再多说了,前面讲面向组件编程思想的部分里面已经充分展示出了这两点优点。在这里我解释一下第三和第四点。

  如今的很多大型软件,都不可能由某一个人单独开发,甚至不会由某一个公司去单独开发。这是因为现在的很多大型软件,综合性太强,涉及的面也太广。而一个人的精力是有限的,不可能学会这么多方面的知识,也不可能掌握到这么多方面的编程技术,即使有可能,这样做的效率也是很低下的。所以,通常的情况是分工协作。仍以前面提到的网上办公管理系统为例,这个系统分为了三个模块:主控模块、通信模块和数据库模块。由于这三个模块具有相当的独立性,那么就可以将现有的所有开发人员分为三组,每一组负责一个模块。而这三组之间,只需要商量好相互间的接口就可以了。这样,对于每一个开发人员来说,就不需要掌握所有的编程技术,甚至不需要了解其他模块的具体实现,而软件仍然能有效的开发成功。这就是所谓的便于软件开发的分工协作了。

   除此之外,如果一个大型的软件希望允许用户在一定程度上定制自己的应用,那么COM也是最好的选择。比方说一个软件由两个模块组成,模块A和模块B,现在软件的开发商希望给予用户一定的灵活性,希望可以允许用户自己定制模块B来实现自己特定的应用,那么就只需要公开模块B的所有接口;而用户自己编程实现模块B时也只需要实现了所有的这些接口就行了。当然,这里面还有很多问题,比如COM组件的注册,这涉及到COM标准的一些细节,在这里不作讨论。

COM中的几个重要概念

组件

  这里所说的组件,就是前面反复在讨论的所谓模块。现在我只想强调一下组件需要满足的一些条件。首先是封装性,组件必须向外部隐藏其内部的实现细节,使从外部所能看到的只是接口。然后是组件必须能动态链接到一起,而不必像面向对象中的class一样必须重新编译。

接口

    由于组件向外部隐藏了其内部的细节,因此客户要使用组件时就必须通过一定的机制,也就是说要通过一定的方法来实现客户与组件之间的通信,这就需要接口。所谓接口就是组件对外暴露的、向外部客户提供服务的连接点。外部的客户见不到组件内部的细节,它所能看到的只是接口,客户也是通过接口来获取组件提供的服务。这有点像OSI网络协议分层模型,每一层就像一个组件,它内部的实现细节对于其他层是不可见的;而每一层通过服务接入点向其上层提供服务,这就像这里所说的接口。一般来说,接口总是固定的,也是公开的。组件的开发人员要实现这些接口,而客户则通过接口获得服务正是接口的这种固定和公开,才使得组件和客户能够在不了解对方的情况下达成一致。

客户

  这里所说的客户不是指使用软件的用户,而是指要使用某一个组件的程序或模块。也就是说,这里的客户是相对组件来说的。

COM的实现原理与雏形模拟

  COM编程的一个重要特点就是要模块化,说得具体一些,就是要将客户和组件分隔开来,而客户和组件之间又是通过接口来通信的。下面,我就介绍一下COM是怎样将客户与组件分隔开来,又是怎样利用接口来实现客户与组件间的通信的。

  首先我要讲讲接口。COM中的接口实际上是一个函数地址表,当组件实现了这个接口后,这个函数地址表中就填满了组件所实现的那些接口函数的地址。而客户也就是通过这个函数地址表获得组件中那些接口函数的指针,从而获得组件所提供的服务的。从某种意义上说,我们可以把接口理解为c++中的虚拟基类;或者说,在c++中可以用虚拟基类来实现接口!这是因为COM中规定的接口的存储结构,和c++中的虚拟基类在内存中的结构是一致的。其存储结构如下图:   

                                         虚函数表
               vtbl指针------>Fun1()指针-------->
                                       Fun2()指针-------->
                                       Fun3()指针-------->
                                       …………   
  Vtbl指针指向一个虚函数表,而这个虚函数表的表项就是指向这些虚函数的指针。

  接口有了,那么组件又是怎样实现接口的呢?实际上,如果用虚拟基类来实现接口,那么组件就是对这个虚拟基类的继承。大家知道,当某个类继承于一个虚拟基类的时候,它就要实现这个虚拟基类里声明的虚函数,这就正好与组件实现接口这一点相吻合。举一个例子来说明,有一个接口InterfaceA,组件ComponentB要实现这个接口,那么就可以这样用c++语言来描述:

[cpp] view plaincopy
  1. //接口:  
  2. class InterfaceA  
  3. {  
  4.   virtual void Fun1()=0;  
  5.   virtual void Fun2()=0;  
  6. };  
  7. //实现了接口InterfaceA的组件:  
  8. class ComponentB: public InterfaceA  
  9. {  
  10.   virtual void Fun1()  
  11.   {  
  12.      printf("Fun1\n");  
  13.   }  
  14.   virtual void Fun2()  
  15.   {  
  16.      printf("Fun2\n");  
  17.   }  
  18. };  
  19.   //而客户只需要得到一个指向ComponentB实体的InterfaceA指针就可以获得ComponentB组件的服务了:  
  20. //使用了组件ComponentB的客户:  
  21. //……  
  22. ComponentB CB;  
  23. InterfaceA *pIA=&CB;  //获得指向ComponentB实体的InterfaceA指针,以下客户就可以只通过接口来获取组件的服务  
  24. pIA->Fun1();  
  25. pIA->Fun2();  
  26. //……  

  但是我们注意到,这样做组件ComponentB和客户还是没有被完全分隔开。因为在客户代码里需要创建ComponentB实体,这对于只能看到接口而对组件一无所知的客户来说,是不可以接受的(比如客户不会知道组件的类名叫ComponentB)。解决这个问题的方法是在实现组件的动态链接文件(比如dll文件)里创建组件的实体,而不是在客户代码里创建组件实体。通常组件都是以dll的形式出现的,而在实现组件的dll里都会实现一个叫CreateInstance的函数,这个函数可以被外部的客户调用。它返回一个接口的指针,当客户调用这个函数后就能够获得指向组件实体的接口指针了。它的实现也很简单:

[cpp] view plaincopy
  1. //在实现组件ComponentB的dll里:  
  2. InterfaceA *CreateInstance()  
  3. {  
  4.    ComponentB CB;  
  5.    InterfaceA *pIA=&CB;  
  6.    return pIA;  
  7. }  
  8. //当然,真正的CreateInstance函数没有这么简单,我上面的代码只是一个简单的模拟。有个CreateInstance函数之后,客户代码就变成了:  
  9. //使用了组件ComponentB的客户:  
  10. //……  
  11. InterfaceA *pIA=CreateInstance();  //获得指向ComponentB实体的InterfaceA指针,以下客户就可以只通过接口来获取组件的服务  
  12. pIA->Fun1();  
  13. pIA->Fun2();  
  14. //……  

  这样,组件和客户就完全被分隔开了,而连接它们的只有接口以及一个CreateInstance的函数。

  以上就是COM的基本原理了。当然,我前面也说了,COM其实是一套规范,它定义了很多标准,比如COM规定每个接口都必须继承于一个叫IUnknown的接口。我这里基本上没有提及它的这些标准,只是希望能通过对它进行一个简单的模拟来说清楚它的实现原理。下面就给出我模拟COM机制实现的一套COM的雏形,希望能对大家理解COM有帮助。

[cpp] view plaincopy
  1. //1、实现了组件ComponentB的ComponentDll.dll:  
  2. //Interface.h  
  3. //接口  
  4. class InterfaceA  
  5. {  
  6. public:  
  7.   virtual void Fun1()=0;  
  8.   virtual void Fun2()=0;  
  9. };  
  10. //Component.h  
  11. //组件(实现了接口InterfaceA)  
  12. class ComponentB: public InterfaceA  
  13. {  
  14. public:  
  15.  virtual void Fun1()  
  16.  {  
  17.   printf("Fun1\n");  
  18.  }  
  19.  virtual void Fun2()  
  20.  {  
  21.   printf("Fun2\n");  
  22.  }  
  23. };  
  24. //ComponentDll.cpp  
  25. //CreateInstance函数  
  26. ComponentB instance;  
  27. extern "C" _declspec(dllexport) InterfaceA *CreateInstance()  
  28. {  
  29.  InterfaceA *pIA=&instance;  
  30.  return pIA;  
  31. }  
  32. //2、客户Client.exe:  
  33. //Client.cpp  
  34. #include "Interface.h"  
  35. #pragma comment(lib,"ComponentDll")  
  36. int main(int argc, char* argv[])  
  37. {  
  38.  InterfaceA *pIA=0;  
  39.  pIA=CreateInstance();  
  40.  if(pIA!=0)  
  41.   pIA->Fun1();  
  42.  return 0;  
  43. }  
  44.  

    [COM/ATL]组件、对象、MFC、ATL的区别

    分类: 那些杂项 401人阅读 评论(0) 收藏 举报

    目录(?)[+]

    组件(Component)和对象(Object)之间的区别

    先明确组件(Component)和对象(Object)之间的区别。组件是一个可重用的模块,它是由一 组处理过程、数据封装和用户接口组成的业务对象(Rules Object)。组件看起来像对象,但不符合对象的学术定义。它们的主要区别是: 

    1. 组件可以在另一个称为容器(有时也称为承载者或宿主)的应用程序中使用,也可以作为独立过程使用; 
    2. 组件可以由一个类构成,也可以由多个类组成,或者是一个完整的应用程序; 
    3. 组件为模块重用,而对象为代码重用。现在,比较流行的组件模型有COMComponent Objiect Module,对象组件模型)/DCOM( Distributed COM,分布式对象组件模型)和CORBACommon Object Request Broker Architecture,公共对象请求代理体系结构)。到这里,已经出现了与本文相关的主题COM,而CORBA与本文无关,就不作介绍。 

    MFCATL之间的不同

    目前MFCATL代表了两种框架,分别面向不同类型的基于Windows的开发。

    • MFC代表了创建独立的Windows应用的一种简单、一致的方法;ATL提供了一种框架来实现创建COM客户机和服务器所必须的样板文件代码。
    • ATL则不同于MFC,它完全面向COM组件,其技术路线也不同于MFCMFC使用的是C++中的继承、封装、嵌套等常规技术,而ATL使用了C++中模板、多继承等高级技术,甚至还用到了STL。所以学习和使用ATL要求我们必须熟悉这些C++高级特性。

    另一方面,ATL结构完全针对COM中的诸多规范,这就要求使用人员必须非常了解COM规范,才有可能真正把ATL用好。

    什么是ATL

    ATL(动态模板库)ActiveX Template Library 的缩写,它是一套C++模板库。基本目标就是使COM应用开发尽可能地自动化

    ATLActiveX Template Library 的缩写,它是一套C++模板库。使用ATL能够快速地开发出高效、简洁的代码(Effective and Slim code),同时对COM组件的开发提供最大限度的代码自动生成以及可视化支持。为了方便使用,从Microsoft Visual C++ 5.0版本开始,MicrosoftATL集成到Visual C++开发环境中。19989月推出的Visual Studio 6.0 集成了ATL 3.0版本。目前,ATL已经成为Microsoft标准开发工具中的一个重要成员,日益受到C++开发人员的重视。 

    1. ATL的基本目标就是使COM应用开发尽可能地自动化,这个基本目标就决定了ATL只面向COM开发提供支持。目标的明确使ATLCOM技术的支持达到淋漓尽致的地步。对COM开发的任何一个环节和过程,ATL都提供支持,并将与COM开发相关的众多工具集成到一个统一的编程环境中。对于COM/ActiveX的各种应用,ATL也都提供了完善的Wizard支持。所有这些都极大地方便了开发者的使用,使开发者能够把注意力集中在与应用本身相关的逻辑上。
    2. ATL因其采用了特定的基本实现技术,摆脱了大量冗余代码,使用ATL开发出来的COM应用的代码简练高效,即所谓的“Slim Code”ATL在实现上尽可能采用优化技术,甚至在其内部提供了所有C/C++开发的程序所必须具有的C启动代码的替代部分。同时ATL产生的代码在运行时不需要依赖于类似MFC程序所需要的庞大的代码模块,包含在最终模块中的功能是用户认为最基本和最必须的。这些措施使采用ATL开发的COM组件(包括ActiveX Control)可以在网络环境下实现应用的分布式组件结构。
    3. ATL的各个版本对Microsoft的基于COM的各种新的组件技术如MTSASP等都有很好的支持,ATL对新技术的反应速度大大快于MFCATL已经成为Microsoft支持COM应用开发的主要开发工具,因此COM技术方面的新进展在很短的时间内都会在ATL中得到反映。这使开发者使用ATL进行COM编程可以得到直接使用COM SDK编程同样的灵活性和强大的功能。

0 0
原创粉丝点击