XPCOM解决方案
XPCOM允许开发者把软件项目分解成模块,这就是所谓的组建,在运行时被组装在一起。XPCOM的目标就是使得各个模块可以独立开发。为了组件在程序中的互操作性,XPCOM把组件的实现和接口分离。但是XPCOM也提供一些工具和库用于加载和操作组件和服务,使得开发者能写出支持版本和跨平台的代码,这样组件就可以被替换或升级而不是重建程序。使用XPCOM,开发者开发的组件可以在不同的程序使用,或者替换程序的功能XPCOM不仅仅支持组件开发,还提供了平台开发的大部分功能,比如:1.组件管理2.文件抽象3.对象消息传递4.内存管理以下章节将详细介绍,现在,我们只需要把XPCOM看成是一个组件开发平台。
Gecko
即使在某些方面和微软的COM结构相似,XPCOM设计用于应用程序级别的。XPCOM最主要用在Gecko。XPCOM是访问Gecko库和嵌入或者扩展Gecko的方法。
Gecko用在很多网络应用程序,最多的是浏览器。
Components
XPCOM允许你创建一个大系统,把它分解成多个模块。这就是组件,很小的,可重用的二进制库,可以包含一个或多个组件。当一个二进制库包含两个或者多个相关的组件,这个库则称为模块。把软件分为多个组件方便开发和维护。
Interfaces
把软件分解为多个组件是好的想法,正确的做法如何呢。最基本的思想是识别模块的功能和理解模块之间的通信。模块之间的通道构成了模块之间的边界,边界正式化之后就是所谓的接口。
接口在编程中并不是一个新想法。在我们的第一个“hello world”程序中就已经用到接口。接口允许开发者封装实现和软件内部工作机制。客户不需要了解事情是怎么做的。
Interfaces and Encapsulation
组件之间的边界,抽象对软件维护和重用至关重要。假设,一个类封装不好,使用了公开的初始化方法,
class SomeClass
{
public:
// Constructor
SomeClass();
// Virtual Destructor
virtual ~SomeClass();
// init method
void Init();
void DoSomethingUseful();
};
该系统要正常工作,程序员必须关注组件是怎么创建的。该未封装的类的契约 是:一组定义方法什么时候可以调用和用于做什么的规则。一条规则指定 在调用 Init()之后才能调用DoSomethingUseful。DoSomethingUseful做一些检查,确保
Init()已经先被调用。
除了编写良好的注释代码告知开发者init的规则之外,开发者还需要使得契约更简洁。首先,对象的创建可以被封装,DoSomethingUseful定义为虚函数。这样创建和初始化对用户就完全是不可见的。在这种半封装的情况下,类唯一暴露的部分只是一组可调用的函数。一旦类被封装,用户可见的接口如下:
class SomeInterface
{
public:
virtual void DoSomethingUseful() = 0;
};
实现可以继承该类并实现虚函数。代码的使用者可以用工厂模式创建一个对象并进一步封装实现。在XPCOM中,通过这种方法对客户隐藏组件内部实现,依赖接口访问提供的功能。
The nsISupports Base Interface
在组件和基于接口的编程中根本的两个问题是组件生命周期,也叫对象所有权和接口查询,或能够识别哪个接口组件在使用。本节介绍基本的接口,XPCOM中所有接口的母接口——nsISupports。它为以上的两个问题提供了解决方法。
Object Ownership
在XPCOM中,由于组件可能实现多个接口,接口必须引用计数。组件必须记录有多少客户端与之关联,当引用计数为0时将自己删除。当创建组件时,内部的整型数进行计数。客户端实例化组件时引用计数自动自增,在组件的生命周期,引用可以增加或减少,一直大于0.某种情况下,所有的客户端不再需要组件,引用计数变位0,组件将自身删除。
当客户端负责人的使用接口,这个是很简单的过程。XPCOM有工具使之更为简单。它可能引发一些管理问题,比如,一个客户端使用了一个接口,但是忘记把引用计数减少。这样的话接口就不会被释放而导致内存泄漏。引用计数系统就像客户端和实现之间的契约。如果遵守则能很好的工作,否则就会出错。创建接口指针的函数要负责初始化引用计数。
nsISupports支持了接口查询和引用计数的基本功能。QueryInterface , AddRef , 和Release 提供了基本的方法来从对象中获取接口,增加引用计数和释放对象。
class Sample: public nsISupports {
private:
nsrefcnt mRefCnt;
public:
Sample();
virtual ~Sample();