線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:3111
推到 Plurk!
推到 Facebook!

想了解這是何種技巧(關於介面)

答題得分者是:Coffee
alvin26
初階會員


發表:24
回覆:41
積分:28
註冊:2005-04-12

發送簡訊給我
#1 引用回覆 回覆 發表時間:2012-03-21 17:16:23 IP:210.59.xxx.xxx 訂閱
 各位大大,
小弟之前追vcl程式,
TXMLDocument.SaveToFile,
追進去看到
DOMPersist.save(FFileName),
再追到 save 裡面是個介面:
IDOMPersist 的 save ,
像這種把物件指給介面,
用介面來操作的技巧不知叫什麼名稱?
可以介紹幾本書讓小弟研究這種技巧嗎?
感謝
------
弟子規,聖人訓,首孝悌,次謹信,泛愛眾,而親仁,有餘力,則學文。
編輯記錄
alvin26 重新編輯於 2012-03-22 02:34:26, 註解 無‧
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#2 引用回覆 回覆 發表時間:2012-03-21 19:07:28 IP:220.130.xxx.xxx 訂閱
這算是個大哉問,我不熟OOP,只能回個大概。

1. 關於Delphi Win32的Interface(Delphi 7以前),並不是一個純粹的Interface,它的實作最主要是為了要讓Delphi擁有使用Microsoft COM的能力,最上層是IUnknown,你可以找一下IUnknown這個interface在Delphi文件及Micrososft文件裡面的相關內容。當你利用QueryInterface去詢問該instance是否實作了IDOMPersist的時候,它就會回給你一個IDOMPersist的實體,讓你可以有這個interface的function可以呼叫。這是在你無法得知該instance是否實作某個interface時使用的方法,也是COM的主要結構之一。

2. 就一個純粹的Interface來說,Thinking in Java的作者說,你可以想成是一個契約。這個契約約定了使用這個介面的人必須有相同的function prototype,也就是輸出入介面將會相同,而實作則視各類別需要而可以有不一樣的實作,只要達到契約的目的即可。
因此IDOMPersist這個interface約束了所有實作IDOMPersist

正因為interface並沒有實際的實作,因此多重繼承最顯而易見的成員函式衝突的問題就不存在。
如IDOMPersisst有Save();這個函式,我自己也實作另一個ICustomStream,它也要求了Save()這個函式,雖然兩者都要求,但兩個目的是一樣,因此完成了這兩個interface要求的契約。

3. 就基礎來說,物件導向的程式語言本來就鼓勵使用抽象層類別(不一定是要interface),透過操作抽象層類別或介面來達到統一問題處理流程,又讓下層的實作可以各自保有各自必要的特性。
如你舉的例,實作IDOMPersist的save實際上只是個空殼,當我呼叫save的時候,XML的類別可以去實作將內部xml資料存成xml檔,HTML的類別可以將資料存成html檔。

在Delphi最容易找的例子就是在dpr檔內找到Application.CreateForm,它傳入的參數是TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
不管我實作的Form的類別叫什麼樣子,它的class的上層都有一個TComponentClass,而它內部確保實作TComponent,從而確保當我在呼叫CreateForm的時候可以正確的被初始化。而DataModule上層也正是一個TComponent,所以當你選擇在程式開始時就初始化DataModule時,Application.CreateForm也能正確的初始化DataModule。

在Java最常舉的例子是排序,一個自訂類別當它需要可以被排序時,它必須去實作IComparable(在C#也是同一個interface name),好讓其它使用到排序的類別知道如何去對這個自訂類別使用CompareTo去排序。
在某些狀況下,也會有沒有明顯的實作IComparable介面,而是單純的提供CompareTo函式的狀況發生,這時候為了測試以及確保該類別真的有提供此函式而且可以被排序,Reflection與RTTI就因此產生了。(當然從這邊可以再衍生出一點wrapper的味道出來)

4. 回到系統/架構面來說,以上層/抽象類別的操作來統一所有的動作為基礎的,有一個觀念叫作控制反轉(IoC),你可以參考這個連結並看看其它有資料:http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=1857



我能理解的大概就是這樣,如有錯誤煩請不吝指教。


------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
編輯記錄
Coffee 重新編輯於 2012-03-21 05:12:23, 註解 無‧
alvin26
初階會員


發表:24
回覆:41
積分:28
註冊:2005-04-12

發送簡訊給我
#3 引用回覆 回覆 發表時間:2012-03-23 10:24:46 IP:210.59.xxx.xxx 訂閱
 感謝 Coffee 大大熱心的解答,
這樣宏觀的視角,讓我有了更整體的概觀,
不至於落入瞎子摸象的窘境。
我大概知道我要往哪個方向去研究了,非常感謝~
------
弟子規,聖人訓,首孝悌,次謹信,泛愛眾,而親仁,有餘力,則學文。
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#4 引用回覆 回覆 發表時間:2012-03-23 16:14:41 IP:221.120.xxx.xxx 訂閱
因為沒有人吐我槽,我自己來吐自己。
利用QueryInterface取得的是Reference(參考),這件事就COM的架構來說也很重要。對於COM object的生命週期是取決於Reference count(某些JVM GC實作機制也是類似的方式),當利用QueryInterface取得Reference時,該COM object的ref count會 1,所以在使用完此參考後必須使用Release來對ref count -1,當COM object的ref count用盡時,該object就會被回收。而Reference並不是實體,真正的instance還是原本的COM object。
太久沒寫都忘的差不多,應該還有很多強者可以吐我槽才對。
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
編輯記錄
Coffee 重新編輯於 2012-03-23 02:15:25, 註解 無‧
P.D.
版主


發表:571
回覆:3884
積分:3672
註冊:2006-10-31

發送簡訊給我
#5 引用回覆 回覆 發表時間:2012-03-24 11:21:32 IP:118.169.xxx.xxx 未訂閱
Coffee 兄, 我都看不懂你們在談什麼, 怎麼敢吐糟呢? 
===================引 用 Coffee 文 章===================
因為沒有人吐我槽,我自己來吐自己。
利用QueryInterface取得的是Reference(參考),這件事就COM的架構來說也很重要。對於COM object的生命週期是取決於Reference count(某些JVM GC實作機制也是類似的方式),當利用QueryInterface取得Reference時,該COM object的ref count會 1,所以在使用完此參考後必須使用Release來對ref count -1,當COM object的ref count用盡時,該object就會被回收。而Reference並不是實體,真正的instance還是原本的COM object。
太久沒寫都忘的差不多,應該還有很多強者可以吐我槽才對。
leveon
資深會員


發表:30
回覆:386
積分:303
註冊:2012-02-12

發送簡訊給我
#6 引用回覆 回覆 發表時間:2012-06-02 14:53:56 IP:118.165.xxx.xxx 訂閱
寫個範例大家就能瞭解基本原理


type
IMyInterface = interface
['{D4BBAE4C-CCE1-4790-A329-7DFBD85C8EE4}']
procedure P1;
end;

TForm1 = class(TForm,IMyInterface)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure P1;
public
{ Public declarations }
end;
//Form 1上 P1 方法
procedure TForm1.P1;
begin
showmessage('From Form1');
end;
//透過Interface 呼叫P1
procedure TForm1.Button1Click(Sender: TObject);
const
{ Library ID }
LibraryUID :TGuid = '{D4BBAE4C-CCE1-4790-A329-7DFBD85C8EE4}';
var
MyIntf:IMyInterface;
begin
// QueryInterface
Form1.QueryInterface(LibraryUID,MyIntf);
MyIntf.P1;
MyIntf:=nil;
//這樣也行
(Application.FindComponent('Form1') as IMyInterface).QueryInterface(LibraryUID,MyIntf);
MyIntf.P1;
MyIntf:=nil;
end;

優點: 1.減少 unit 互相uses
2.更好的封裝
缺點: 架構不確定時 容易規劃不當 程式更迂迴
編輯記錄
leveon 重新編輯於 2012-06-02 00:54:31, 註解 無‧
系統時間:2017-11-20 12:00:29
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!