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 但不會釋放自身。