【必修】软件复用与组件技术 学习笔记(二) chapter2 COM、接口

(一)COM

COM规范就是一套为 组件构架设置 标准的文档。
【可以看做是组件的连接方式,组件可以被重写,被按照需求进行实现,但是构架设置不会轻易改变。】

1.COM的组成:

COM组件是动态链接库(DLLs)或者可执行文件(EXEs)的形式发布的可执行代码组成的。

遵循COM规范编写的组件能够满足对组件架构的所有需求。

COM由接口和实现两部分组成。
1) 实现部分对于客户来说是黑盒,客户能够看到的是组件的接口部分;

2) 接口部分则由若干接口组成。

a) 每个接口是有序排列的一组函数指针;可以简单地理解为一组函数;
b) 每一个接口相当于该组件对外的一个窗口,客户可以通过不同的视角(接口)看到,获得该组件提供的服务;
c) 对客户来说,组件就是接口集,只能通过接口与组件交互。

2.COM的作用:

COM是动态链接的(在运行时使组件之间发生关系)。COM使用DLL将组件动态链接起来。

3.COM的要求:

COM组件必须进行封装,它们可以被封装因为COM满足如下限制条件:
1) 与语言无关;
2) 可以以二进制形式发布;
3) 可以在不妨碍老顾客的情况下被升级;
4) 可以透明地在网络上被重新分配位置。对远程机器上的组件同本地机器上的组件的处理方式没有差别。

4.COM的作用:

1) 给其他应用程序提供面向对象的API或服务的好方法;
2) 建立快速构造应用程序,与语言无关的组件库的建立;

5.COM库

COM具有一个被称为是COM库的API,它提供的事对所有客户及组件都非常有用的组件管理服务。
COM库中的大多数代码均可以支持分布式或网络化的组件。

6.COM方法

我们可以把COM作为一种编写程序的方法。
比如,可以在任何操作系统上使用任何编程语言按COM风格进行编程。

7.COM满足用户需要

COM使用DLL来提供可在运行时被替换掉的组件,COM保证这些组件可以充分利用动态链接带来的好处,它使用如下方法来实现这一点:
1) 提供了一个所有组件都应该遵守的标准;
2) 允许使用组件的多个不同版本;(这一点对用户几乎完全透明)
3) 使得可以使用相同的方法来处理类似的组件;
4) 定义了一个与语言无关的架构;
5) 支持对远程组件的透明连接。

COM强制开发人员必须将客户与组件严格地隔离开。

8. COM的封装

COM通过组件与客户之间的连接或接口来实现封装。(下一节为“接口”)
————————

(二)接口

1.接口的定义

计算机程序实际上是通过一组函数而连接起来的,这组函数实际上就定义了程序中不同部分的接口。

比较常见的接口,比如DLL接口,C++接口,DLL的接口就是它所输出的那些函数,C++类的接口则是该类的一个成员函数集(后面会提到)

而COM的接口,涉及到一组由组件实现并提供给客户使用的函数,对于COM来说,接口是一个包含一个函数指针数组的内存结构。每一个数组元素包含的是一个由组建所实现的函数的地址。

在C++中,可以使用抽象基类来实现COM接口。由于一个COM组建可以支持任意数目的接口,所以我们将用抽象基类的多重继承来实现它。

2.接口的作用

在COM中,接口就是一切。对于用户来说,一个组件就是一个接口集。

3.接口实现了可复用的程序构架

接口连接着被实现的模块,模块可以被任意实现,但接口相当于它们的组织方式,连接方式意味着积木最终会被搭建成什么样子。

4.接口的其他优点

接口使得用同样的方式来处理不同的组件,这种能力就被称作是多态

5.COM接口的实现

#include <iostream>
using namespace std;

class IX  
{
	public:
		virtual  void Fx1() =0;
		virtual  void Fx2() =0;
 } ;
 
class IY
{
	public:
		virtual void Fy1() =0;
		virtual void Fy2() =0;
};

class CA:public IX
{
	public:
		virtual void Fx1()	{ cout<<"Fx1"<<endl;}  //重写 
		virtual void Fx2() { cout<<"Fx2"<<endl;}
		virtual void Fy1()  { cout<<"Fy1"<<endl;}
		virtual void Fy2()  { cout<<"Fy2"<<endl;}
};

int main()
{
	CA c;
	c.Fx1();
	c.Fx2();
	c.Fy1();
	c.Fy2();
}

组件CA使用 IX 和 IY 实现了两个接口。

其中,IX 和 IY是用于实现接口的纯抽象基类(仅包含纯抽象函数的基类)

(纯虚函数是指用 =0 标记的虚拟函数。)

在定义纯抽象函数的类中,是不实现它们的。

我们可以将抽象类看作是一个表单,派生类所做的就是填充此表单的空白。抽象基类制定了其派生类应该制定那些函数,而派生类则具体实现这些函数。 对纯虚函数的继承被称作是接口继承,这主要是因为派生类所继承的只是基类对函数的描述,抽象类并没有提供任何可供继承的实现细节。

比如,在上面这个例子当中,IX 和 IY 就是纯抽象基类,它们的FX1和FX2函数就是没有实现细节的纯虚函数,但在 IX 和 IY 中列出了表单,就是可以在派生类,也就是(CA)当中的实现。

最后,由于COM接口是与语言无关的,所以它对接口有一个二进制的标准要求,为了使一个纯抽象基类真正成为接口,它必须继承一个名为IUnknown的借口。

6.编码约定

为将接口同其他类的定义区分开来,作者使用了几种不同的编码约定——

1)在接口名称的前面,加上一个大写字母“I”;

2)在类的前面,加上一个大写字母“C”;

3)此外,大多数情况下我们并不将一个接口定义成一个类,而是使用了Microsoft Win32 软件开发工具SDK中头文件中的定义:

#define interface struct

在这个定义之下,不需要另外在类中加入 public 关键字。因此,以上例子可以重新定义如下:

#include <iostream>
#include <objbase.h>
using namespace std;

class IX
{
//	public:  (去掉了public) 
		virtual  void Fx1() =0;
		virtual  void Fx2() =0;
 } ;
 
class IY
{
//	public:
		virtual void Fy1() =0;
		virtual void Fy2() =0;
};

class CA:public IX
{
	public:
		virtual void Fx1()	{ cout<<"Fx1"<<endl;}  //重写 
		virtual void Fx2() { cout<<"Fx2"<<endl;}
		virtual void Fy1()  { cout<<"Fy1"<<endl;}
		virtual void Fy2()  { cout<<"Fy2"<<endl;}
};

int main()
{
	CA c;
	c.Fx1();
	c.Fx2();
	c.Fy1();
	c.Fy2();
}

注意,在这个过程当中,IX 和 IY ,也就是纯抽象基类的Public被去掉了,但是实现类CA 中的public并没有被去掉。

7.一个完整的例子(非动态连接)

【把这个代码详细扒出来】
**①首先,引入#include <objbase.h>,**这个库是为了用 interface.

interface:
interface是面向对象编程语言中接口操作的关键字,功能是把所需成员组合起来,用来装封一定功能的集合。它好比一个模板,在其中定义了对象必须实现的成员,通过类或结构来实现它。接口不能直接实例化,即ICount ic=new iCount()是错的。接口不能包含成员的任何代码,只定义成员本身。接口成员的具体代码由实现接口的类提供。 接口使用interface关键字进行声明。

main函数相当于用户——我个人的理解是“调用实现接口的类的地方才是客户”,不知道是否正确,待验证。

③在抽象接口之中有一个标记 __stdcall ,这个标记的作用是使用标准的调用决定,即这些函数将在返回到调用者之间将参数从栈中删除。
因为在常见的C或C++之间的调用约定中,栈的清理工作是由调用者完成的。

#include <iostream>
#include <objbase.h> //引入interface 
using namespace std;

void trace(const char * pMsg) {
	cout <<pMsg <<endl;
}

//抽象接口
interface IX
{
	virtual void __stdcall Fx1()=0;
	virtual void __stdcall Fx2()=0;
 } ;

interface IY
{
	virtual void __stdcall Fy1()=0;
	virtual void __stdcall Fy2()=0;
};

class CA:public IX,public IY
{
	public:
	virtual void __stdcall Fx1() {cout <<"CA::Fx1" <<endl;	}
	virtual void __stdcall Fx2() {cout <<"CA::Fx2" <<endl;	}
	
	virtual void __stdcall Fy1() {cout <<"CA::Fy1" <<endl;	}
	virtual void __stdcall Fy2() {cout <<"CA::Fy2" <<endl;	}
};

//Client
int main()
{
	trace("Client:Create an instance of the component.");
	CA* pA = new CA;
	
	//Get an IX pointer
	IX* pIX = pA;
	
	trace("Client:Use the IX interface.");
	pIX->Fx1();
	pIX->Fx2();
	
	//Get an IY pointer
	IY* pIY =pA;
	
	trace("Client:Use the IY interface.");
	pIY->Fy1();
	pIY->Fy2();
	
	trace("Client:Delete the component.");
	delete pA;
	
	return 0;
 } 

在这里插入图片描述

总结

1.COM接口在C++中是用纯抽象基类来完成的;

2.一个COM组件可以提供多个接口。

3.一个C++类可以使用多继承来实现一个可以提供多个接口的组件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值