Delphi 6 的 Interface(50分)

To 吴剑明

>> 1 类似与抽象类的作用。 _/
>> 2 可以实现类似与多重继承的功能。 ?
>> 3 INTERFACE的实现必须要有类来实现他的接口。 _/

>> 不知道理解的对不对? :)

 
其实在COM里面,当我们调用CreateComObject函数时,就已经建立了一个类实例,大概类似
与下面语句:
m := TMMC.Create as IMMC;

然后被AS IMMC。要能够被AS转换,必须有GUID,对吧?只是在非COM程序里面,这个GUID不会
被注册到注册表里。
还有TInterfacedObject的主要作用是什么?不会就是为少写那几个方法吧?
 
佩服得说,我从来没有碰过Soap,对COM,Corba只是有些肤浅认识,就在语言方面
做点补充吧。

1. 关于 多重继承 和 接口 。

事实上应该从继承说起,继承意味着子类具有父类所有的成员变量和成员方法,
(从本质而言,父类是作为子类的一个内嵌类存在的),那么多重继承也就意味
着子类具有好几个父类的所有成员变量及成员方法,这本身没有什么玄妙的地方,
但是一旦出现一种“菱形”结构, 即类 d1, d2 继承于类 b, 而类 mi 又多重继
承了类 d1,d2 ,则 mi 类中则会包含有两个 b 类的内嵌类,在做从 mi 到 b
的向上映射时,会出现二义性(C++ 用了一种虚继承的机制来避免这种情况)。

我个人认为很多时候不应该把接口和多重继承放在一起谈,因为接口没有成员
变量,没有实现了的成员方法,它根本没办法作为另一个类的内嵌成员存在。也就
是说没有什么类可以“继承”接口,只有“实现”接口。

写完上面的东西我中午出去溜达了一圈,买了本《COM本质论》(南京的朋友可
以在军人俱乐部内的科海书店用75折买到),先大概看了该书序言,里面一段话于
我心有戚戚,hoho,摘录这段话,希望不会被人告侵权:

“实际上应该存在两种继承性。实现继承(implementation inheritance )表示
真正的实现(即行为)被继承;而接口继承( interface inheritance )则表示
只有行为的规范被继承”

以这个角度来理解的话,Java 和 Delphi 里的接口 还有 C++ 里的纯虚基类
都是提供了接口继承, 但是千万不要以为这是多重继承的替代,我想有必要强调这
一点: 只有在作向上映射时,接口才能发挥作用。

2. 关于 接口 和 设计模式

设计模式中关于接口的最最最最直观运用是 Adapter 即 适配器 模式,而
Adapter 在Observer 模式中也起到了相当重要的应用,强调这两个模式有两个原

一, 这两个模式我最常用,也可以说别的模式我用得很少或者根本不会用。
二, 我觉得这两个pattern 适合初学模式的入手,也很容易从中发现pattern
及面向对象的妙处。
 
to cheka 兄:
> “我个人认为很多时候不应该把接口和多重继承放在一起谈”
这个说法有点问题。您号召大家看的 Thinking in Java, 2nd Edition 第8章开场就说,
interface is more than just an abstract class taken to the extreme,
since it allows you to perform a variation on C++’s “multiple inheritance,”
实际上 Java 引入接口就是为了变相实现多重继承的。

我需要反复强调的是 COM 的 Interface 和 Java 的 Interface 完全是两回事请。
而 Delphi6 的 Interface 正在从前者变化到后者。(这是我发此问题的用意)

> “接口没有成员变量”
这个概念目前已经变化,我记得前一段看到 JDK1.4 将允许接口成员变量。
不过现在一时忘记出处,所以暂时没有办法证明。等我再去找找,以及看一下 D6 的情况。
 
to 吴剑明兄:
在COM里面,用 GUID 去实现 m := TMMC.Create as IMMC;
的实质是用了 IDispatch 的 QueryInterface 方法,QueryInterface 方法需要 GUID,
这个您可以参考这个问题:http://www.delphibbs.com/delphibbs/dispq.asp?LID=605058

Java 的 Interface 的实质是:The fact that they exist in Java indicates
that they were considered important enough to provide direct support
through language keywords. (引自 Thinking in Java, 2nd Edition 第8章开场白)
换成中文,说白了就是我上面长贴里说的“Interface作(成)为基本语言要素”。

我再次强调的是 COM 的 Interface 和 Java 的 Interface 完全是两回事请。

D6 的 Interface 用到 SOAP 的例子中,我曾经想 SOAP 与 COM 也许没有关系,就说自说自
话,定义 Interface 时,没有写 GUID, ['{697F01D4-1994-4743-8B96-752F28DE4610}'],
结果不行,程序运行不了。这是为什么?我目前正在调查中,有知情的朋友也望告知。

当然,当时不写 GUID 是因为我不知道如何插入那 ['{x-x-x-x}'] 那个东西,现在知道了。
按 ctrl+shift+g.
 
to yysun:
SOAP我没用过,不过运行不了,并提示要有GUID, 则肯定里面有称为interface querying
的行为存在, 如object as interface.
 
胜读十年书啊!
 
to yysun:
刚才我回去看,你说d6比起其他有了很大的不同, 已上升至java 的INTERFACE
不知可否更详细点. 没方法,我手头没有d6.
 
我前面的话确实不妥,主要是因为我比较偏向把“继承”(这是比较狭隘的继承,
下面可以看到其实它对应的只是行为继承)和“多态”的概念尽可能分开,而
interface主要是利用了多态的特性。

我想做点修正应该要好理解的多:


首先还是要引用那一段话

“实际上应该存在两种继承性。实现继承(implementation inheritance )
表示真正的实现(即行为)被继承;而接口继承( interface inheritance )
则表示只有行为的规范被继承”

在 C++ 中,无论是实现继承还是接口继承都存在“多重继承”,而在Delphi
和 Java 中,即使引进了 Interface 也只能替代了接口继承方面的“多重继承”。

比方说看这段 C++ 代码

/* 注意两个类声明中的方法都不是虚拟方法,所以只能纯粹的进行实现继承
而不可能有接口继承 */

class CDelphiBBS
{
void HasExcellentDelphiForum()
// DFW有个很棒的Delphi论坛
};

class CCSDN
{
void HasExcellentCPPForum()
// CSDN有个很棒的C++论坛
};


CDelphiBBS::HaveExcellentDelphiForum()
{
.... // 实现
}
CCSDN::HasExcellentCPPForum();
{
.... // 实现
}


那么我们现在可以用一个新的类 CNewForum, 来假设DFW和CSDN
合并以后的新论坛(只是举例,不是说我希望DFW和CSDN合并)

class CNewForum:CDelphiBBS, CCSDN
{
}

那么这个新论坛不需要任何实现(因为在其父类中已经实现了),已
经包括了DFW的Delphi Forum 和CSDN的C++ Forum。

以上说的是实现继承(即行为继承),再来看接口继承,以 Java
代码来演示

interface IGoodDelphiForum //本来想用IDelphiBBS,后来觉得不合适
{
public void HaveExcellentDelphiForum()
// 方法没有实现
}

interface IGoodCPPForum
{
public void HasExcellentCPPForum()
// 方法没有实现
}

这两个Interface 就像某个老板对伙计说:“哪,听着,大富翁的
Delphi论坛很不错,CSDN 的C++ 论坛也很不错,我们现在要做一个新
论坛,综合他们的优点”,作为伙计的你即使觉得老板也许是痴人说梦,
但为了饭碗也得照办,可是你不知道大富翁和CSDN他们本身是如何实现
的,你所知道的只有“规范”(Specification)——即老板的指标
“既有很棒的Delphi论坛也有同样棒的C++论坛”,所以一切都得你卷
起袖子重头做

class NewForum implements IGoodDelphiForum, IGoodCPPForum
{
public void HaveExcellentDelphiForum()
{
.... // 方法的具体实现
}
public void HasExcellentCPPForum()
{
.... // 方法的具体实现
}
}

当这个论坛做完并且有了相当的人气后,在大多数 Delphi 的爱好者
眼里,只需要利用 NewForum 对象的 IGoodDelphiForum 接口;对许多 C++
爱好者来说,则他们的眼里也只有 IGoodCPPForum 接口,他们之间可以
老死不相往来,当然你也可以两个接口都用,到处走走。

当然将来可能有别的人也依照同样的规范来实现他们自己的新论坛

例子到此结束。

顺带说一句,很有趣在COM中的接口最多只能有一个父接口,而Java中的
接口是可以extends 多个父接口的,而后者与 Design Pattern中的interface
概念是真正一致的。在Delphi5之前,如果有

IDerived = interface(IBase1,IBase2);

编译器是会报错的,因为那时候 Delphi 中的接口完全是为了COM, 从每个
方法加个 stdcall 后缀也能看出来。

如yysun所说的话,我猜Delphi6中的interface 应该也实现了像Java这样
的接口“多重继承”了吧?

TO 吴剑明

COM 中的接口都有两个名字,一个是逻辑名,即通常所见到的以 I 打头的
如IMyInterface, 还有一个是实质名,即GUID。引入GUID 是因为来自不同厂商
的接口很可能会有相同的逻辑名,单纯利用逻辑名来调用对应的接口是不保险的,
而GUID则安全的多

令 TO yysun

Java 中的Interface 可以包含成员变量,我刚查的,以前没留意,不过这
种成员变量都被隐含定义为static与final, 这就好理解了。

 
谢谢 cheka,使得大家对 interface 有了更加深刻的理解。

恕我再多舌两句:

1、按照 Design Pattern 的核心思想,就是为了避免“一切都得你卷起袖子重头做”。
您可以根据 IGoodDelphiForum, IGoodCPPForum 的规范来设计您的 class
NewForum,而不必管那两个接口的实现类是具体如何实现的。(这种规范并不是指标)

在CDelphiBBS 和 NewForum 之间放置 IGoodDelphiForum 的目的是如果有 CCSDNforDelphi
类也实现了 IGoodDelphiForum 接口,您的 NewForum 可以任意选择采用一个实现类来构建
您的 NewForum,而不需要任何改动 NewForum 本身太多。

C++ 这方面确实有点不如 Java,至少是不如 Java 方便。

2、Java 中的 Interface 的成员变量
以前是定义为 static final。Java 中的 static final 相当于 C++ 的 #define。没有
语法上实际意义的。

但是,我记 JDK1.4 将允许真正的接口成员“变量”。象 int i
这样的呢。
重大变革,不是吗?
 
是否就是说D5后的程序都可通过Interface来与诸如com等进行互操作呢?!
若是,其操作机制又是如何的?!
举例说明
 
怪我前面没有描述清楚,类 NewForum 是对两个接口的实现,而不是接口
的客户程序。

“卷起袖子重头做”当然是对接口的实现者而言的,对接口的客户程序来
说,眼中只需要有接口就可以了, 以接口的“不变”来应其实现类的“万变”。

另外我不是很明白 JDK1.4 为什么要将非 static final 的成员变量引入接口,
得好好想想,如果引入的不是成员变量而是属性的话(就像Delphi的property)
我倒不觉得奇怪
 
Dephi6在接口中仍然使用TGuid,纯粹是为了兼容COM,
使用Delphij的本地接口,实际上只是使用了TGUID记录结构的D1和D4部分4+8字节.
如果你的接口的唯标号([:)]我管这个东西叫做唯标号,你们跟着我叫吧.:))
中间两部分(D2,D3)不同,而D1D4相同,这两个不同的唯标号,在有些时候是会出错的.
 
To yysun:
刚刚看了一下D6里的接口实现,好像并没有你说的那么大的变化啊?
基本的套路还是和以前D5差不多,倒是VCL里面针对接口的实现的层次
放到更底层,大概为了kylix的原因。显著的变化好像就是一个Property而已
使用方式上变化也不大……
 
我支持cheka的观点——interface无法解决实现继承的多重继承。所以java里面有个
delegate的做法,比较烦
 
我的理解:在DELPHI5中实现COM是聪明的
COM本身就是OO的 可扩充 可重用。。。
通常OBJECT是一种把数据和操作数据的方法(METHOD)封装在一起的数据类型实例
,相反COM则是使用interface
接口,实际是纯虚类,只有定义没有实现(implementation)
COM中的接口都继承自IUnknown,以保证每个接口都有QueryInterface函数来返回接口指针
DELPHI做COM还涉及一些其他如GUID、CLSID、IID
以下简单介绍COM开发流程(语焉不详请大家谅解):
1。NEW ITEM-》ACTIVEX-》ACTIVEX LIBRARY
2。NEW ITEM-》ACTIVEX-》COM OBJECT(添加一个COM类)
然后可以用TYPE LIBRARY来定义项目中变元
//如果您是先霸王硬上MIDAS,再来看COM,你也许就会感受到DELPHI为COM做的
包装是如此方便
但要留意的是这里COM的接口是DELPHI为您多重继承而来的纯虚接口
离题:火柴棒”在您的精心布置下,能玩出多少花样还要看你自己的造化,了解COM推荐一本
《WIN2000编程实用大全》,URL除了MSDN暂时没有
注:对D6了解不是很全面,还请yysun、cheka等虾斧正
 
建议到深度论坛上看看李维专区有这方面的贴子
 
翻了翻 delphi 技术手册 有这么一段:
"
教科书常常将继承关系描述成 "is a"关系,例如 TSaveingAccount "is a" TAccount.

但在教科书之外,简单的"is a"关系被打破了。正方形是矩形的一种,但并不意味着需要
从TRectagnle继承TSquare。矩形是一种多边形,但你可能不愿意从TPolygon继承TRectangle。
类继承迫使派生类存储所有在基类中声明了的字段,但在这种情况下,派生类并不需要那样
的信息。TSquare对象可以避免为所有的边都存储一个长度。但是,一个TRectangle对象必须
存储两个长度。一个TPolygon对象需要存储许多边和顶点。

解决办法是将类型继承(正方形是一种矩形,矩形是一种多边形)与类继承(类C继承类B的字段
和方法)分离开来。在类型继承时使用接口,这样你就可以把类继承留到他最有用的地方:继承
字段和方法。

..."

另外我的感觉是:
Delphi通过接口实现了多重继承,而C++不需要接口也可以很好的实现多重继承.
所以C++中没有将接口作为基本要素,而Delphi必须要。(可能Java也类似)
 
顶部