Delphi面向對象學習隨筆八:物理封裝

作者:巴哈姆特
http://www.cnpack.org
(轉載請注明出去并保持完整)
前面說過的封裝其實是邏輯意義上的封裝。邏輯封裝是對某一特定邏輯功能模塊的封裝,這個特定邏輯功能塊可以是一個類,當然也可以是一個包,他們都有自己的邏輯邊界。另一種封裝方式,我們通常叫它為物理封裝:物理封裝其實是具體實現代碼的物理集合,他可以以bpl,dll,com+等形式體現。

??? 邏輯封裝里,對象的傳遞、數據共享與調用相對要簡單的多,只要我們引用類所定義的單元(unit)就可以直接訪問類中public和published所公布出來的屬性或方法,在編譯的時候,編譯器會把工程內所有引用的單元全部打包到exe中。邏輯封裝最終是以一個獨立的物理文件存在的。雖然簡單,但是無法實現物理上的切割,一旦其中某個單元或代碼段發生改動,那么其他的單元或代碼段也需要重新編譯和連接。

??? 而在物理封裝中,對象的傳遞、數據共享與調用要復雜的多了,由于在編譯的時候,exe和dll或bpl是兩個或多個文件,所以你無法像在邏輯封裝中那樣簡單的uses那個unit。而物理封裝的好處是可以減少維護量,因為每個dll都是動態調用的,所以,我們只需要更新我們改動過的相應的dll,而其他的部分則可以不用改動。

? 用DLL封裝對象:
??? 用DLL封裝函數,我想幾乎是所有程序員熟悉到不能再熟悉的技術,而且我們可以找到很多相關的書籍和資料。這里我們只討論怎么用DLL來封裝對象。
??? 用DLL封裝對象有以下的好處:
????? 一、可以節約內存。我們可以在使用到DLL資源的時候動態裝載,不用時釋放。
????? 二、提高代碼重用。DLL在封裝好以后,我們可以使用任何一個支持DLL的開發工具來調用它。
????? 三、可以使軟件拆分成若干個小塊,這樣可以有效的降低維護量。
??? 注意:如果你只為了減少軟件體積而使用動態庫,那么我建議你還是放棄使用動態庫吧。

??? 當然,想使用DLL封裝對象也有一定的困難:
????? 一、調用DLL的EXE只能使用DLL中對象的動態綁定的方法。
????? 二、DLL中的對象只能在DLL中創建。
????? 三、在DLL和調用方,都需要對封裝的對象和被調用的方法進行聲明。

??? 我們來看下面的例子:

??? 首先我們聲明一個類:

typeTNewClass = class(TObject)publicprocedure SayHello; virtual;// 注意,這里不能使用靜態方法,必須使用動態綁定(或者說晚綁定)技術。// 至于為什么——虛方法表有關,大家可以找其他資料詳細研究^_^end;procedure TNewClass.SayHello; // 實現部分
beginShowMessage('Hello');
end;

?

新建一個Library項目

library dll;function GetObj: TNewClass; stdcall;
begin // 創建對象Result:= TNewClass.Create;
end;exports GetObj; // 定義輸出函數
end.

?

下面,我們創建一個EXE工程,并且添加類的聲明:

typeTNewClass = class(TObject)publicprocedure SayHello; virtual; abstract;{ 注意這里的聲明方式和DLL中的不同,這里必須聲明為virtual方法,還有由于此方法通過晚綁定用的是DLL中的實現,因此EXE中可不寫其實現而聲明成abstract方法。 }end;function GetObj: TNewClass; stdcall; external 'dll.dll';

?

之后,我們可以添加一段測試代碼來測試我們是否實現了DLL對象的共用:

varNewClass: TNewClass;
beginNewClass:= GetObj;if not Assigned(NewClass) thenExit;tryNewClass.SayHello;finallyFreeAndNil(NewClass);end;
end;

?

我們可以看到,這的確達到了EXE與DLL之間傳遞對象的目的。
??? 但是,有點麻煩:首先,在DLL工程與EXE工程都需要有被封裝對象的定義。其次,virtual和abstract必須正確使用。還有,如果一旦對象發生變化,那么兩邊的定義都需要修改,這樣難免會出點小錯。

??? 其實,我們可以使用接口來進行對象的傳遞,上面的例子我們可以稍微修改一下:

??? 首先,我們定義一個接口:

typeINewInterface = interface(IInterface)procedure SayHello(); // 定義我們要的方法end;

?

另外,修改TNewClass類的聲名:

typeTNewClass = class(TInterfacedObject, INewInterface)publicprocedure SayHello();end;

?

實現部分無須改動。
??? 接著,我們修改先前的那個Library項目:

library dll;function GetObj: INewInterface; stdcall;
begin // 創建對象Result:= TNewClass.Create;
end;exports GetObj; // 定義輸出函數
end.


在EXE工程中,我們直接引用接口定義的單元,并且修改輸出函數的聲明:

function GetObj: INewInterface; stdcall; external 'dll.dll';

?

之后,測試代碼會成這樣:

varNewInterface: INewInterface;
beginNewInterface:= GetObj;NewInterface.SayHello;NewInterface:= nil;
end;

?

這樣做的好處是,我們可以避免在多處重復說明一個要傳遞的對象的聲明,只要我們需要的方法的聲明方式不動,我們只需要改動TNewClass的實現代碼,而無需改動EXE程序中的任何代碼部分。
??? PS:Delphi的OpenToolsAPI接口就是這個通過接口共享對象原理的很典型的應用。(這是劉嘯說的。老實說,這個用法是我在寫這個筆記的時候臨時想到的,因為從來沒有使用過未經COM封裝的interface。哪里知道竟然和OpenToolAPI一樣的原理,自己YY下^_^)

??? 當然我們還可以使用COM來封裝對象:
??? 首先,我們建立一個名為NewCom的COM模型,建立COM模型前一篇已經說過,這里不再重復。
??? 那么,我們的調用代碼就會變成這樣:

varNewCom: ITNewCom;
beginNewCom:= CoTNewCom.Create;// 當然,和我前一篇一樣使用CreateComObject函數也是一樣的NewCom.SayHello;NewCom:= nil;
end;

?

我們可以看到,實現代碼幾乎沒什么改動。那么,假如我們什么時候要把SayHello的實現代碼:

ShowMessage('Hello');

?

改成:

MessageBox(0, 'Hello', 'SayHello', MB_OK);

?

??? 那么,我們只需要更新這個COM文件,調用它的EXE程序無須改動,這就是接口的優點。

轉載于:https://www.cnblogs.com/keyvip/archive/2010/11/06/1870632.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/378885.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/378885.shtml
英文地址,請注明出處:http://en.pswp.cn/news/378885.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java 方法 示例_Java掃描器具有示例的NextNextShort()方法

java 方法 示例掃描器類的hasNextShort()方法 (Scanner Class hasNextShort() method) Syntax: 句法: public boolean hasNextShort();public boolean hasNextShort(int rad);hasNextShort() method is available in java.util package. hasNextShort()方法在java.…

mysql創建表時出現1071_mysql 出現1071錯誤怎么辦

mysql 出現1071錯誤的解決辦法:該問題是由鍵值字段長度過長導致。mysql支持數據庫表單一鍵值的最大長度不能超過767字節,超出這個長度即報錯(見標題名稱)。一般情況下,不會有鍵值字段的長度會超出該長度。但是需要注意的是,隨著全…

srs rtmp從監聽到接收到新連接的過程分析

:分析srs從啟動開始的main入口開始。分析rtmp監聽端口,然后到接收到新rtmp連接的過程分析。 目錄一、流程圖二、代碼分析1、srs_main_server.cpp:main()2、srs_main_server.cpp:do_main()3、srs_main_server.cpp:run()4、srs_main_server.cpp:run_master…

OpenCV筆記(基于Python)

學習時,將每塊注釋分別去掉即為一個模塊知識點 圖像數字化 import numpy as np """ #二維的ndarry #兩行四列全是0的uchar類型的二維數組 z np.zeros((2,4),np.uint8) print(type(z)) print(z) print("\n")#兩行四列全是1的整型矩陣 o …

POJ 1222 1681 1830 3185 開關燈問題 (高斯消元 異或方程組)

POJ 1222 EXTENDED LIGHTS OUT 基本的開關燈問題.還保證唯一解. 我們把每一個燈泡當成一個狀態xi,總共有30個,而且每個燈與其他燈的關系也很明顯。所以我們就可以列30方程30個變元的方程組: xi 1 * xi 1 * x(i-1) 1 * x(i1) 1 * x(i-6) …

我看周馬,以及3Q大戰背后的社會問題

如今鬧得不可開交的3Q大戰已經成了一道獨特的風景線,讓我們在茶余飯后又增添了不少談資。這兩個中國最大的客戶端軟件提供商各有擁躉無數,雙方鉚足了勁相互吐口水、扔磚頭,現在貌似到了動刀子了。周、馬在媒體上也都將自己標榜為“美貌與智慧…

Java PushbackReader ready()方法與示例

PushbackReader類ready()方法 (PushbackReader Class ready() method) ready() method is available in java.io package. ready()方法在java.io包中可用。 ready() method is used to check whether this stream is ready to be read or not. ready()方法用于檢查此流是否已準…

mysql數據庫知識點梳理_MySQL數據庫知識點整理 (持續更新中)

一、修改用戶密碼格式(在命令行下輸入):mysqladmin -u 用戶名 -p舊密碼 password 新密碼1. 給root添加密碼ab12: mysqladmin -uroot -password ab122. 將root的密碼修改為djg345: mysqladmin -uroot -pab12 password djg345二、添加新用戶格式:grant…

加載一張照片,可選擇是否另存為

加載一張照片,按下S鍵保存,ESC退出 加載一個灰度圖(E:\Python-workspace\yanyu.png),顯示圖片按下’s’鍵保存(beyond.png)(保存后的路徑和該程序所在路徑一致)后退出,或者按下 ESC 鍵退出不保存 import cv2img cv2.imread(E:\…

RTMP代理的協議規范(RtmpProxy)

RtmpProxy 關于RTMP代理的協議規范。RTMP是字節協議,第一個包是c0,1個字節,一般是03表示是明文的RTMP。所以如果需要做RTMP代理,如果直接轉發RTMP客戶端的消息,是沒法傳遞額外的信息的,譬如HTTP代理在Head…

經典地址收集

http://kuler.adobe.com/ 配色網站轉載于:https://www.cnblogs.com/Wolves/archive/2010/11/08/1871914.html

Java Math類toDegrees()方法與示例

數學類toDegrees()方法 (Math class toDegrees() method) toDegrees() method is available in java.lang package. toDegrees()方法在java.lang包中可用。 toDegrees() method is used to convert an angle from radians to degrees. toDegrees()方法用于將角度從弧度轉換為度…

談談Hybird3D中的光柵化優化

看到空明流轉分享了他的SALVIA 0.5.2優化談,我也來說說Hybird3D中和光柵化相關的一些優化技術。 Hybird3D的設計目標是打造一款準實時的軟件高質量渲染器,采用了光柵化和光線跟蹤混合算法,光柵化用于渲染eye ray,光線跟蹤則用于陰…

RTP協議基本分析(RTSP、WebRTC使用)

目錄1、介紹2、RTP3、格式4、RTP打包H2644.1、H264打包方式之Single NAL Unit4.2、H264打包方式之FU-A4.2.1、FU indication4.2.2、FU header4.2.3、第一個IDR幀的NALU第一個切片4.2.4、第一個IDR幀的NALU第二個切片4.2.5、第一個IDR幀的NALU最后一個切片5、RTP打包AAC5.1、AU-…

對照片進行邊緣化處理,并將邊緣化處理后的結果保存

對照片進行邊緣化處理,并將邊緣化處理后的結果保存 import cv2 from matplotlib import pyplot as plt img cv2.imread(E:\Python-workspace\OpenCV\OpenCV/water1.png,1)#第一個參數為選擇照片的路徑,注意照片路徑最后一個為正斜杠其他都為反斜杠&…

小皇帝,籃球,熱火

失敗,又一次,完全預料之中. 熱火的防守早已是千瘡百孔,熱火的攻擊也是亂無頭緒. 現在的熱火,需要詹姆斯無球的跑動,需要韋德的助攻。 轉載于:https://www.cnblogs.com/JeffChen/archive/2010/11/12/2600335.html

fastjson轉換時有大括號或者冒號或者有中括號_[Python Basic] 字符串處理以及類型轉換 1...

String Manipulation & Typecasting (1)1. 文本復制以及連接1.1 Multiply sign使用 multiply sigh/乘號* 來復制文本片段。乘號復制文本舉例: print("Hi" * 3) # output: HiHiHi print("*" * 10)# output:**********1.2 連接1.2.1 使用 plu…

Java IdentityHashMap size()方法與示例

IdentityHashMap類的size()方法 (IdentityHashMap Class size() method) size() method is available in java.util package. size()方法在java.util包中可用。 size() method is used to return the size (i.e. How many key-value pair exists) of this IdentityHashMap. siz…

讀《深入分析Java Web技術內幕》

這里這本書的預讀章節,看完預讀部分,解答了一些疑惑,也相信這是一本夯實Java Web架構體系的好書。 HTTP協議解析 開發一般使用firefox的firebug調試,這的確是一個利器,HTTP的請求頭響應頭一目了然。 瀏覽器緩存機制 當…

windows mobile多國語言實現[轉]

介紹一種多國語言的實現辦法,這也是微軟推薦的方式,打開windows mobile下的windows目錄可以看到有很多以MUI為后綴名的文件,例如shellres.dll.0804.mui、shell.dll.0804.mui。。。。。。我們可以用eXeScope.exe或者resources hacker這樣的文件…