Delphi/Lazarus 裡面有個概念就是接口,這個概念在很多語言裡面也都存在,而且發揚,dot Net 就是這樣。
提到接口自然而然就想到COM、DCOM、OLE、ActiveX等相關知識,不錯,確實可以應用於這些環境,而且Delphi/Lazarus中的Interface功能強大到不僅實現微軟基於COM的技術,在Linux或其他系統下Interface的設計理念和方法一樣可用。這裡我們僅僅記錄一下在Windows下的心得。
Delphi/Lazarus下面向對象設計中多態、繼承、封裝利用Interface的方法和Class的方法一般會有所區別,很早有本書中就提到“在設計初期就必須明確,用接口開發,還是Class開發”。那麼接口到底是什麼呢?在高級語言中我們可能會比較模糊,與純抽象類有毛線區別啊,都是“方法定義嘛”!這個就要從其內部結構說說了。
其實Interface在計算機內存中就是一片連續的方法說明,整個很關鍵哦,也就是為什麼接口定義了,就不可以再隨意更改了。必須重新定義新的接口,就算加一個功能也是如此。尤其是多個程序、多中語言進行溝通更是如此。按這個邏輯推導方法的順序也不可以隨意調整哦。
Delphi/Lazarus中接口很多我們大多會繼承自IUnknown /IInterface,對它們是等價的。他們其中的3個方法也是COM的基礎。
該代碼是 FPC 下的,所以定義了很多條件編譯開關IUnknown = interface
['{00000000-0000-0000-C000-000000000046}']
function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;
out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
function _AddRef : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
function _Release : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
end;
IInterface = IUnknown;
而我們在使用中也往往會繼承 TInterfacedObject,作為實現的父類,它畢竟幫我們實現了最基本的釋放功能。也就是引用記數到0就釋放資源。這樣問題也就來了。也就是實現了資源的自動管理,這和dotNet不是一樣嗎,當然不同了,dotNet畢竟是後起之秀,晚好多年咧,都是安德森設計的所以資源控制上更厲害,但萬事萬物都有其兩面性,我有時就是不想讓它自動釋放!“明明是智能釋放哪裡會有這樣的情況發生為什麼不用呢,記數到0就該釋放”要解釋這個情況不能從簡單Class來解釋,需要用比較復查的結構來描敘。
Type
TGroup=class;
IGroup=interface(IUnknown)
procedure K1;
end;
IClient=interface(IUnknown)
procedure K2;
end;
TClient=class(TInterfacedObject,IClient)
public FGroup:IGroup;
procedure K1;
end;
TGroup=class(TInterfacedObject,IGroup)
public FClient:IClient;
procedure K2;
end;
var
_ClientObj:TClient;
_GroupObj:TGroup;
_Client_IF:IClient;
_Group_IF:IGroup;
. . .
begin
_ClientObj:=TClient.Create;
_GroupObj:=TGroup.Create;
_ClientObj.FGroup:=_GroupObj;
_GroupObj.FClient:=_ClientObj;
_Client_IF:=_ClientObj;
_Group_IF:=_GroupObj;
_Client_IF:=nil; // OK
_Group_IF:=nil; // Error
end.
(dotNet也是自動釋放,後面章節也會提到如何使用dotNet而不自動釋放),在Delphi/Lazarus中這時可以選擇繼承來自TComponent,它也實現了IUnknown 但不會釋放自身。