淺談Interface
多重繼承 OOP 的重點在於繼承,封裝,多型等概念上,其中以繼承最受爭議,尤其是多重繼承與單一繼承,到目前為止,至少在實作上許多語言都選 擇了單一繼承,原因不外乎多重繼承會將整個物件架構複雜化,為了 保留多重繼承的特性, Interface(介面) 的概念就成為最好的選擇, 標準的OO 繼承課題就是父母與子女之間的關係,子女繼承了父與 母的的特性,此為多重繼承,但我們都知道,現實上子女並不會擁有父 母的所有特性及能力,但在多重繼承概念上,子可以向上轉型為父,也 可以向上轉型為母,而這就是多重繼承受爭議的地方. 圖:多重繼承
由上面的圖我們可以發現,子同時繼承了父與母兩個類別,也就是說 子擁有了父與母類別所有的特性,讓我們以一個較簡單的方式來說, 當父類別擁有抽煙的特性,那在多重繼承的觀念下,子類別必然也有 抽煙的特性,但我們都知道這並不是絕對的.因此我們需要讓子類別 選擇是否會抽煙,基於這個理由,我們得把父類別的抽煙特性定義成 可覆載,這樣子類別才能選擇是否會抽煙,如果這類特性不多的話還 好,但多的話就很煩人了,所以多重繼承下的結果,必定是很沉重的. 多重繼承的替代品 - Interface
圖:以Interface 實作取代繼承
從上圖來看,你可以發現子直接繼承人類別,而不是繼承父或母,那子類別如何擁有父與母的特性呢? 例如子類別要擁有抽煙的能力? 上圖中我們稱之為子類別繼承了人類別並實作了抽煙這個介面,我 們也可以說子是個人,擁有抽煙的能力,這樣的做法是否比上面的多 重繼承更符合現實呢? 呵,我把這個問題留給你,我可不想再一次陷 入論戰中,回到Interface,基本上Interface 也擁有了繼承特性,你可以 繼承抽煙這個Interface,並加入新的特性 圖:Interface 繼承
我知道這不太雅,呵,你就將就一下吧 :)
因此我們重新定義一下父母子的關係
圖:實作繼承的Interface
Interface 繼承與Class 繼承是差不多的,只是Interface 繼承了定義而非實體類別也可以實作多個Interface,例如下圖:
DELPHI(PASCAL) 與Interface基本上DELPHI 支援Interface 操作,但在DELPHI 6 之前的操作較不 直覺,因此容易造成DELPHI 對Interface 支援不足的假象,慶幸的是 在DELPHI 6 中這個問題已經被解決了,這也使得Interface 成為 WebSnap 最重要的部份,同時也在VCL 中有相當重的戲份,下面是 你最常看到的Interface 運用: IMyInterface=interface ['{FE function SayHello:string;end; TMyObject=class(TInterfacedObject,IMyInterface) function SayHello:string;end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private procedure DoSayHello(Intf:IMyInterface); { Private declarations } public { Public declarations }end; varForm1: TForm1; implementation {$R *.DFM} function TMyObject.SayHello:string;begin Result:='Hello';end; procedure TForm1.DoSayHello(Intf:IMyInterface); begin ShowMessage(Intf.SayHello);end; procedure TForm1.Button1Click(Sender: TObject);var Obj:TMyObject; Intf:IMyInterface;begin Obj:=TMyObject.Create;Intf:=(Obj as IMyInterface); DoSayHello(Intf);end; OK,我想這個範例大家都看過了,接下來我們變點不一樣的 type IMyInterface=interface ['{FE function SayHello:string;end; TMyObject=class(TEdit,IMyInterface) function SayHello:string;end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private procedure DoSayHello(Intf:IMyInterface); { Private declarations } public { Public declarations }end; varForm1: TForm1; implementation {$R *.DFM} function TMyObject.SayHello:string;begin Result:='Hello';end; procedure TForm1.DoSayHello(Intf:IMyInterface);begin ShowMessage(Intf.SayHello);end; procedure TForm1.Button1Click(Sender: TObject);var Obj:TMyObject; Intf:IMyInterface;begin Obj:=TMyObject.Create(Self);Intf:=(Obj as IMyInterface); DoSayHello(Intf);end; 嘿! 我可沒要你照著打哦,這樣是不會通過編譯的,你得變成這樣才行(在DELPHI 5). procedure TForm1.Button1Click(Sender: TObject); var Obj:TMyObject; Intf:IMyInterface; begin Obj:=TMyObject.Create(Self);Obj.GetInterface(IMyInterface,Intf); DoSayHello(Intf); Obj.Free; end; GetInterface 是用來取得我們想要的Interface,基本上它會傳回一個Boolean 代表是否取得了Interface, 如果你在DELPHI 6 的話,之前未修改過的那一個版本就可以通過編譯器,所以啦,DELPHI 6 還是進步了. 其實在DELPHI 6 中正規的寫法是這樣 procedure TForm1.Button1Click(Sender: TObject); var Obj:TMyObject; Intf:IMyInterface; begin Obj:=TMyObject.Create(Self);if Supports(Obj,IMyInterface,Intf) then DoSayHello(Intf); Obj.Free;end; 如果你在DELPHI 5 中這樣寫也可以,不過你會發現Supports 除了繼承至TinterfacedObject 之外的物件都會傳回False, 這是因為DELPHI 5 的的TComponent 並未實作Iunknown(至少在明定上沒有),但在DELPHI 6中,TComponent 實做了相當於Iunknown的Iinterface, 所以如果你想要在你的程式中完整運用Interface,建議你還是用DELPHI 6 會較為直覺. 用DELPHI 5 的話還可使用下面的方法來通過編譯器 TMyObject=class(TEdit,IMyInterface,IUnknown) 這樣你就可以使用as 來轉型,可是如果你要使用Supports 的話,Compiler 會丟出一個錯誤,因此還是使用GetInterface 來的方便一點!
做這件事是很簡單的,一個類別可以實做一個已上的Interface,這我想你一定早就知道了, 因此在這裡我提一下有關實作多個Interface 時的運用.
轉型規則 一個類別實作了一個已上的Interface 時,例如X類別實做了A,B,C 三個Interface,因此你可以透過X 取得 A, 同樣的,你也可以透過A 取得 B或透過A 取得C,這在COM 中有很詳細的定義: Symmetric(對稱) 當你透過A 成功取得 B,那麼你也可以透過B 成功取得A
Transitive(遞移) 當你透過A 成功取得B,且透過B 成功取得C,那麼你也可以透過A 成功取得C. Reflexive(反身) 使用A 查詢 A 必定是成功的. Delegation(代理)
從DELPHI 4 開始,OO PASCAL 就支援Interface Delegation,這種技術使得實作Interface 變的更有彈性,你可以從下面的範例中看出端倪 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type IMyInterface=interface ['{2E173B2D-6BE9-4519-8E function SayHello:string;end; IMyInterface2=interface ['{3FD6CFDF-E028-4FD6-9834 function SayHello2:string;end; TMyObject2=class(TInterfacedObject,IMyInterface2) function SayHello2:string;end; TMyObject=class(TInterfacedObject,IMyInterface,IMyInterface2) privateFDelgateObj:TMyObject2; publicproperty DelgateObj:TMyObject2 read FDelgateObj implements IMyInterface2; constructor Create; function SayHello:string;end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations }end; varForm1: TForm1; implementation {$R *.dfm} constructor TMyObject.Create;begin FDelgateObj:=TMyObject2.Create;end; function TMyObject2.SayHello2:string;begin Result:='I am Object2';end; function TMyObject.SayHello:string;begin Result:='I am Object1';end; procedure TForm1.Button1Click(Sender: TObject);var MyObject:TMyObject; Intf1:IMyInterface; Intf2:IMyInterface2;begin MyObject:=TMyObject.Create;if Supports(MyObject,IMyInterface,Intf1) then ShowMessage(Intf1.SayHello);if Supports(MyObject,IMyInterface2,Intf2) then ShowMessage(Intf2.SayHello2);end; end.重點就在籃色字及紅色字的部份,你可以發現我們的TMyObject實作了兩個Interface,但你在裡面卻只找到IMyInterface的定義, 這是因為我們運用了Delgation 將IMyInterface2 導向TMyObject2,而它正是實作IMyInterface2 的類別,這種技術使得主類別簡潔許多 ,同時也帶出了另一種運用: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type IMyInterface=interface ['{2E173B2D-6BE9-4519-8E function SayHello:string;end; IMyInterface2=interface ['{3FD6CFDF-E028-4FD6-9834 function SayHello2:string;end; TMyObject2=class(TInterfacedObject,IMyInterface2) function SayHello2:string;end; TMyObject=class(TInterfacedObject,IMyInterface,IMyInterface2) privateFDelgateObj:IMyInterface2; publicproperty DelgateObj:IMyInterface2 read FDelgateObj write FDelgateObj implements IMyInterface2; constructor Create; function SayHello:string;end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations }end; varForm1: TForm1; implementation {$R *.dfm} constructor TMyObject.Create;beginFDelgateObj:=Nil; end; function TMyObject2.SayHello2:string;begin Result:='I am Object2';end; function TMyObject.SayHello:string;begin Result:='I am Object1';end; procedure TForm1.Button1Click(Sender: TObject);var MyObject:TMyObject; MyObject2:TMyObject2; Intf1:IMyInterface; Intf2:IMyInterface2;beginMyObject:=TMyObject.Create; MyObject.FDelgateObj:=TMyObject2.Create; if Supports(MyObject,IMyInterface,Intf1) then ShowMessage(Intf1.SayHello); if Supports(MyObject,IMyInterface2,Intf2) then ShowMessage(Intf2.SayHello2);end; end. 上面這個範例告訴我們,我們可以指派任何實作了IMyInterface2物件給TmyObject,這就是上面所說的另一種運用.
Property In InterfaceDELPHI 6 中的說明檔標示這個是新功能,但事實上,DELPHI 5 就有這個能力了,沒有DELPHI 4,所以不知道她有沒有, 基本上這是讓你可以在Interface 中宣告Property,我們用一個範例開始: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type IMyInterface=interface ['{2E173B2D-6BE9-4519-8Efunction GetSayHello:string; property SayHello:string read GetSayHello;end; IMyInterface2=interface ['{3FD6CFDF-E028-4FD6-9834function GetSayHello2:string; property SayHello2:string read GetSayHello2; end; TMyObject2=class(TInterfacedObject,IMyInterface2) function GetSayHello2:string;end; TMyObject=class(TInterfacedObject,IMyInterface,IMyInterface2) private FDelgateObj:IMyInterface2; public property DelgateObj:IMyInterface2 read FDelgateObj write FDelgateObj implements IMyInterface2; constructor Create; function GetSayHello:string;end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations }end; varForm1: TForm1; implementation {$R *.dfm} constructor TMyObject.Create;begin FDelgateObj:=Nil;end; function TMyObject2.GetSayHello2:string;begin Result:='I am Object2';end; function TMyObject.GetSayHello:string;begin Result:='I am Object1';end; procedure TForm1.Button1Click(Sender: TObject);var MyObject:TMyObject; MyObject2:TMyObject2; Intf1:IMyInterface; Intf2:IMyInterface2;begin MyObject:=TMyObject.Create; MyObject.FDelgateObj:=TMyObject2.Create; if Supports(MyObject,IMyInterface,Intf1) thenShowMessage(Intf1.SayHello); if Supports(MyObject,IMyInterface2,Intf2) thenShowMessage(Intf2.SayHello2); end; end. 我們在Interface 將SayHello,SayHello2 定義為property,而實作這兩個Interface 只需實作Get Method就可以了, 這是否使得Interface 的運用又更方便了呢? DELPHI 6 的Interface 支援已經相當完備了,如果你正巧有DELPHI 6,也正巧要開發軟體, 使用Interface 將會使你的軟體有更高的延展性,當然! 好好規劃也是很重要的.
|


goodgoodstudy
博客统计信息
热门文章
最新评论