零三 發表於 2019-8-22 09:22:00

Delphi 接口

<p><span style="color: rgba(153, 153, 153, 1); font-size: 12px">作者:巴哈姆特<em id="__mceDel">(转载请注明出处并保持完整)</em></span></p>
<p><span style="font-size: 16px">在对象化中,类的继承是一个非常强大的机制;而更加强大的继承机制应该是来自从一个接口的继承。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;本篇我们将讨论接口的特点。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;首先,接口的定义方式与类相似。不同的是:类代表了一种实体,而接口代表了一批操作规范。还有,接口中所有的数据成员都是public访问限制,也就是说,你不能为接口中的数据成员指定其为私有或其他的域成员。另外,接口中的方法只能有声明而不能有实现,因此它看上去更像是一个没有构造和析构方法的纯虚类。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;我看的很多资料中,凡是在介绍接口的时候都会提到“多重继承”,仿佛接口的存在只是为了弥补Object Pascal不支持多重继承而设计的(至少给我的第一印象就是这样),其实接口是非常强大的,也是对象化编程中不可或缺的一个重要组成部分。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;接口之所以强大在于:接口只需要告诉用户方法的名称是什么,有什么参数;而它并不需要理会方法是怎么实现的。例如电脑的构造和工作方式对于一般用户并不重要,因为一般用户更关心的是如何去使用他。所以电脑的接口——鼠标、键盘、显示器等才是用户最关心的地方。那么这就为我们实现对象化最核心的理念——“分离”提供了相当大的便捷。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;首先我们来看看接口的定义方式:下面是Delphi中System.pas里IInterface接口的声名方式</span></p>
<p><span style="font-size: 16px">type</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;IInterface = interface</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;['']</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function _AddRef: Integer; stdcall;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function _Release: Integer; stdcall;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">我们可以看到,和类的基本声明差不多,只是由关键字class改成了interface。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;大家也许会注意到在紧跟在声明后的[''],这是什么呢?这是其实是接口的唯一标识,也就是我们说的TGUID;当把接口注册给系统后,我们可以通过注册表检索到00000000-0000-0000-C000-000000000046这样的键值。那么这就意味着,我们只需要知道一个TGUID的值就可以方便的访问这个接口。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;当然,你可以在接口中定义其他的方法,但是Delphi中是不允许给接口添加变量成员的,因为接口是不允许有实现部分的。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;同样,接口也可以继承、封装以及方法的覆盖。继承接口同类继承类似:</span></p>
<p><span style="font-size: 16px">type</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;INewInterface = interface(IInterface)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// 定义一个新的接口INewInterface,并告诉编译器它是继承自IInterface接口</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;....</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">这里要注意一点,就是我们说过,接口的TGUID是每一个接口的唯一标识,那么也就是说,TGUID是不能重复的。你不能简单的从别处抄袭过来,那样是错误的。如果你需要一个TGUID,你可以在Delphi的代码编辑框中同时按下CTRL+SHIFT+G来获得一个新的TGUID值,这个值是Delphi为你自动生成的。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;接口的继承大致就是这样,和TObject是所有类的根类一样,在Delphi中IInterface是所有接口的根类,那么类似于下面的继承:</span></p>
<p><span style="font-size: 16px">type</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;INewInterface = interface</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;...</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">其实也是定义了一个继承自IInterface的新接口INewInterface(和类名前加一个大写字母T一样,我们习惯于在接口名前加一个大写字母I,当然这只是一个命名约定)</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;我们说了,接口只能有声明,不能有实现。那么怎么让接口为我们工作呢?</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;其实,接口的实现是需要借助于类来完成的(当然这看上去和C++中的多重继承的写法差不多)注意,既然接口是需要借助类来实现的,那么也就是说用来实现接口的类,必须实现接口中所有已定义的方法:</span></p>
<p><span style="font-size: 16px">type</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;TNewInterfaceClass = class(TInterfacedObject, INewInterface)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// TInterfacedObject为类名,INewInterface为我们上面定义的接口名</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;...</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">一般,我们用来实现接口的基类不会选TObject而会选TInterfacedObject,理由是TInterfacedObject类已经帮我们实现了IInterface接口中的方法,我们只需要实现我们自己接口中新的方法就可以了。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;既然我们已经通过类实现了接口中的方法,那么我们就可以使用这个接口来为我们服务了,实例化接口也非常简单:</span></p>
<p><span style="font-size: 16px">var</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewFace: INewInterface;</span><br data-filtered="filtered"><span style="font-size: 16px">begin</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewFace:= TNewInterfaceClass.Create(); // 创建接口</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewFace.xxx; // 调用接口中的方法</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewFace:= nil; // 释放</span><br data-filtered="filtered"><span style="font-size: 16px">end;</span></p>
<p><span style="font-size: 16px">有朋友可能会奇怪,接口的释放为什么只是直接赋为nil?我们前面说过了:接口即没有构造方法,也没有析构方法。既然没有析构方法,那么就意味着我们不能用释放类的方式来释放接口。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;那么直接把接口对象指空会造成内存泄露吗?</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;答案是否定的,因为接口提供了一个引用记数的机制:当某接口实例(也就是实现了这个接口的对象)被引用,比如被赋值给一个接口变量时,该接口实例的AddRef方法会被编译器自动调用,引用计数将增加一。引用取消时编译器则会调用_Release方法将引用计数减一。引用计数减到零时,表示已无其他接口变量引用此接口实例,此时编译器会自动释放它。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;要注意的是,接口对象与类对象是不能混用的。当然像我们上面的例子里,NewFace也只能调用INewInterface接口中所定义过的方法,而不能调用类中定义的不存在于接口中的方法。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;当然,一个类可以同时实现多个接口,多个接口彼此用逗号隔开。如:</span></p>
<p><span style="font-size: 16px">type</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;TNewInterfaceClass = class(TInterfacedObject, IInterface1, IInterface2)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;...</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">同样,实现多个接口的类必须依次实现每个接口中定义的方法。</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;那么,这时出现了一个问题——就是当两个接口中有同名方法怎么办?好办,为他们取别名:</span></p>
<p><span style="font-size: 16px">type</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;IInterface1 = interface(IInterface)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// 接口1</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;fucntion Func(): Boolean;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;IInterface2 = interface(IInterface)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// 接口2</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function Func(): Boolean;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;TClasses = class(TInterfacedObject, IInterface1, IInterface2)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;public</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function IInterface1.Func: Func1;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function IInterface2.Func: Func2;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ 为同名方法起别名 }</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function Func1: Boolean;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function Func2: Boolean;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ 声明方法 }</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">Delphi中还可以使用imploements指示符用于委托另一个类或接口来实现接口的某个方法,有时这个方法又被称为委托实现,关于implements的用法如下:</span></p>
<p><span style="font-size: 16px">type</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;TInterClass = class(TInterfacedObject, IInterface1)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;...</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function GetClasses: TClasses;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;property Face: TClasses read GetClasses implements IInterface1;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;...</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">上面的代码中,implements指示字会要求编译器在Face属性中寻找实现IInterface1接口的方法,属性的类型必须是一个类或一个接口。implements可以指定多个接口,彼此用逗号分隔。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;implements指示字的好处是:</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;一、他允许以无冲突的方式进行接口聚合。(聚合是COM中的概念)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;二、他能够延后占用实现接口所需要的资源,直到确实需要资源。</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;下面是关于委托的一个详细例子(该例在DELPHI7 + WIN2000 SP4 中调试通过):</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;INewInterface = interface(IInterface)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// 定义接口</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function SayHello: string; stdcall;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 接口方法</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;TNewClass = class(TInterfacedObject, INewInterface)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;public</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;function SayHello: string; stdcall;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 第一个类实现接口中的方法</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;TNewClass1 = class(TInterfacedObject, INewInterface)</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;private</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;FNewClass: INewInterface;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;public</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 注意,在这个类中并没有实现接口中的SyaHello方法</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;constructor Create;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;destructor Destroy; override;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;property NewClass: INewInterface read FNewClass implements INewInterface;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 接口对象委托 如果是类对象委托应该是</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// property NewClass: TNewClass read FNewClass implements INewInterface;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;end;</span></p>
<p><span style="font-size: 16px">implementation</span></p>
<p><span style="font-size: 16px">&nbsp;</span></p>
<p><span style="font-size: 16px">function TNewClass.SayHello: string;</span><br data-filtered="filtered"><span style="font-size: 16px">begin</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;Result:= ClassName;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;ShowMessage(Result);</span><br data-filtered="filtered"><span style="font-size: 16px">end;</span></p>
<p><span style="font-size: 16px">&nbsp;</span></p>
<p><span style="font-size: 16px">constructor TNewClass1.Create;</span><br data-filtered="filtered"><span style="font-size: 16px">begin</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;inherited Create();</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;FNewClass:= TNewClass.Create;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// 在构造方法中创建接口</span><br data-filtered="filtered"><span style="font-size: 16px">end;</span></p>
<p><span style="font-size: 16px">destructor TNewClass1.Destroy;</span><br data-filtered="filtered"><span style="font-size: 16px">begin</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;FNewClass:= nil;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;// 在析构方法中释放接口</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;inherited Destroy();</span><br data-filtered="filtered"><span style="font-size: 16px">end;</span></p>
<p><span style="font-size: 16px">调用的例子:</span></p>
<p><span style="font-size: 16px">var</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewInterface: INewInterface;</span><br data-filtered="filtered"><span style="font-size: 16px">begin</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewInterface:= TNewClass1.Create;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewInterface.SayHello;</span><br data-filtered="filtered"><span style="font-size: 16px">&nbsp;&nbsp;NewInterface:= nil;</span><br data-filtered="filtered"><span style="font-size: 16px">end;</span></p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    博客园 滔Roy https://www.cnblogs.com/guorongtao 希望内容对你有所帮助,谢谢!<br><br>
来源:https://www.cnblogs.com/guorongtao/p/11392490.html
頁: [1]
查看完整版本: Delphi 接口