如何使用Delphi开发大型主从架构系统.docx
- 文档编号:17624326
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:21
- 大小:345.41KB
如何使用Delphi开发大型主从架构系统.docx
《如何使用Delphi开发大型主从架构系统.docx》由会员分享,可在线阅读,更多相关《如何使用Delphi开发大型主从架构系统.docx(21页珍藏版)》请在冰点文库上搜索。
如何使用Delphi开发大型主从架构系统
如何使用Delphi開發大型主從架構系統-Package的秘密和威力
相信許多人和我一樣,在使用Delphi開發應用系統的時候,一定會想到如何的切割整個應用系統。
是把所有的子系統撰寫成一個很大(可能會有數M.Bytes的大小)的EXE檔呢?
還是應該把每一個模組撰寫在不同的EXE檔案之中的好。
事實上這兩種方法都有它們自己的問題。
如果是把所有的模組撰寫在一個EXE檔案之中的話,那麼不但執行檔太大,不易更新和維護。
在開發時也不甚方便,因為要讓數個人撰寫同一支應用程式的確比較麻煩。
那麼如果我們把每一個模組讓不同的程式師撰寫成獨立的EXE檔案,再由一個主程式分別啟動不同的EXE檔不就好了嗎?
沒錯這是許多人使用的方法(包括我在內),但是這樣切割應用系統有一個問題,那就是如果每一個獨立的EXE模組,都需要使用資料庫的話,那麼當主程式啟動個別的EXE檔案時,每一個EXE都必須重新再連結到資料庫,開啟資料庫表格,再取得它需要的資料。
這個過程通常都需要不少的時間。
例如連結到Oracle並且開啟一個資料庫表格的話,通常需要五到十秒。
如果EXE開啟的資料庫表格或是查詢比較多的話,那麼主程式在啟動獨立的EXE檔案時,通常需要30幾秒到一分鐘不等。
這對於許多的使用者而言是非常不方便的。
這樣的狀狀甚至會造成你的專案無法交貨(例如使用者要求在五秒之內EXE程式的畫面必須出現)。
除此之外,每一個獨立的EXE又使用了額外的連結以便存取資料庫,造成了資源的浪費。
面對這種二難的局面,你現在的選擇是什麼呢?
事實上這個問題在我的心中也盤旋了許久。
因為這一是我想要解決的問題,只是由於工作的繁忙讓我一直無法花時間解決它。
最近在手上的事情告一段落之後,又接到許多朋友的詢問,所以決定花一些時間試著解決這個重要的問題。
增加應用程式載入的效率
如果我們仔細的思考這個問題的話,就可以發現問題出在每一支獨立的EXE都需要重複的連結資料庫所至。
所以如果我們可以讓連結到資料庫的次數減少的話,不就可以加快應用程式載入的效率了嗎?
這個想法當然很簡單,但是問題是要如何的減少應用程式連結資料庫的次數呢?
事實上這個想法也很簡單,最好是讓應用系統連結資料庫的次數變成一次,如此一來除了主程式需要連結資料庫之外,其他的應用模組都能夠公同使用由主程式載入的資料模組的話,那麼一切問題不都解決了嗎?
請注意,在這裡我所說的其他模組代表獨立的EXE或是其他形式的應用程式,而不是指在單一一個EXE之中不同的表格或是子系統。
我們可以使用圖一來表示這個想法。
在這個構想中,我希望由應用主程式先負責載入公用的資料模組。
在這個資料模組之中有其他子系統需要使用各個資料庫表格,如此一來當主程式啟動其他的子系統時,就不需要再讓每一個子系統再連結,開啟資料庫表格了。
圖一 公用資料模組示意圖
當這樣還有一些設計上的問題,我們稍後再回來討論這個問題,現在先我們看看如何的把這個構想實做出來,並且測試一下實際的結果是不是真的比較有效率。
要實做這個構想,我們必須想辦法讓公用的資料模組能夠讓每一個子系統存取到,並且不再需要每一個子系統都分別的和資料庫建立一個連結的Session。
我的第一個想法是使用DLL來解決這個問題。
但是事實上使用DLL無法解決這個問題。
在我花費了許多的時間之後,使用DLL仍然有會有AccessViolation的錯誤。
我也曾在網路上搜尋相關的問題或是資料,我在寶蘭的DiscussForum中也看到有人提出類似的問題,但是似乎都沒有人確實的回答這個問題。
我也想過乾脆拿出我的Soft-Ice和Bounds-Checker看看為什麼使用DLLAssembly打交道,這實在不是件好玩的事情。
正打算放棄之時,突然想到Delphi3.0之中的Package不正是解決這個問題的好方法嗎?
於是我就決定試試看,果然一擊中地,順利的解決了這個問題。
當然,要知道為什麼使用Package可以解決這個問題,你需要知道Package和DLL的異同。
DLL和Package
為什麼在我一開始使用DLL時無法解決多個模組共用一個資料模組DLL的問題呢?
這主要是因為在Win95/NT中當每一個模組載入DLL時,對於每一個DLL之中的全域變數而言,每一個模組都會有一份獨立的變數。
這是什麼意思呢?
我們可以使用圖二來說明。
圖二Win95/NT中全域變數和模組的關係
當圖二中的模組一和模組二分別的載入它們共用的DLL時,雖然在這個共用的DLL中有一個全域變數gAccount。
但是模組一和模型二會分別的擁有一個gAccount變數。
這也就是說模組一對於gAccount變數數值所做的修改並不會影響模組二中的gAccount變數數值。
在Wn95/NT中的DLL行為是和Win3.x不同的,因為在Win3.x中所有的模組都使是共用一份DLL中的全域變數。
由於Win95/NT中DLL全域變數的這種特性,所以當你想把資料模組撰寫在DLL之中讓多個模組共同使用時,問題就來了。
因為在這種情形下,每一個模組都會有一份它自己的資料模組。
所以當每一個應用程式模組載入資料模組的DLL時,它仍然會連結資料庫一次,所以你並無法減少連結資料庫的次數。
那麼使用Package有什麼不同嗎?
在回答這個問題之前,請你先回想一下。
在Delphi3.x中它允許你使用Package的功能來編譯你的應用程式,如圖三所示。
圖三Delphi3.x允許你使用Package的功能編譯應用程式
使用Package編譯應用程式的好處除了可以減少應用程式的大小之外,事實上Package還有一個很重要的特性,那就是Package允許多個模組共用所有的全域變數。
我們可以使用圖四來說明Package的特性。
請你想一想,當你的應用程式使用Package的功能時,事實上它必須載入許多不同的Packages。
不管你的應用程式是否使用了全域變數Application,許多的Packages都會使用Application這個全域變數。
由於全域變數Application是存在於VCL.DPL之中,所以如果Application會對於每一個載入它的模組都產生一份獨立的全域變數的話,那麼整個應用程式便會產生不正確的結果。
所以由這個說明我們可以知道,在圖四中的Application和Screen等全域變數對於所有使用它的模組而言一定是只有一份全域變數。
圖四Package中全域變數的特性
事實上在Forms.PAS之中的程式碼也透露著這些蛛絲馬跡。
例如下面便是Forms.PAS宣告Application和Screen這二個變數的程式碼。
從它們的注釋中我們可以清楚的看到,它們是全域物件,即使是編譯成Package時也是一樣。
{Globalobjects}
var
Application:
TApplication
Screen:
TScreen
Ctl3DBtnWndProc:
Pointer=nil;
由於Package能夠自動的將其中的全域變數編譯成所有使用它的模組都共用一份的特性。
所以我們就可以使用這個特性重新的建構圖一的架構成為圖五的形式。
圖五改良過的公用資料模組示意圖
在圖五中我們可以把所有模組需要共同使用的資料模組撰寫在一個Package之中,然後再把每一個子系統撰寫成獨立的Package。
只有主程式是EXE,它負責載入共用的資料模組,以及在使用者啟動每一個子系統時再載入相對應的子系統Package。
使用這種架構有許多的好處。
第一個便是共用的資料模組只需要載入一次即可。
而這個好處便是我們前面討論需要解決的問題。
如此一來在每一個子系統載入時便可以加快其執行的速度。
第二個好處是在開發這整個系統時,我們仍然可以讓不同的程式師負責發展不同的子系統,這樣可以就可以解決前面討論的分工的問題。
此外如果一個子系統很龐大的話,你也可以再次的切割這個子系統成為更多的小系統。
例如你可以再把圖五中的會計子系統Package再分為一般會計,成本會計,和管理會計等不同的Package。
第三個好處是使用Package就像是使用DLL一樣,主程式可以在需要的時候才載入一個Package,在使用完畢之後可以立刻的釋放這個Package,這樣可以讓你對於系統資源有更好的控制能力。
最後一個好處是使用Package可以讓你發展出Plug-and-Play的模組,當然這需要藉由結合虛擬介面或是虛擬類別的功能。
藉由使用Package和虛擬介面的能力,你可以任意的替換應用系統之中的模組而不會影響系統的執行。
這個功能對於許多使用Delphi開發套裝軟體的程式師來說是一個非常重要的功能。
現在你應該對於DLL和Package的差異有了基本的瞭解,現在是讓我們看看如何使用Package的特性解決我們面對的問題的時候了。
下一小節就讓我們實際的撰寫一個範例來證明Package對於全域變數的處理方式以及使用Package的確能夠加快應用程式的載入速度。
實際的範例
由於平日我大部份的時間都是使用Oracle,MSSQLServer和InterBase(許多的讀者都詢問我為什麼不使用Sybase或是Informix做為範例說明,這實在是因為我比較少使用它們,並沒有其他的意思,所以請使用Sybase,Informix和DB2的讀者見諒。
不過我相信我們討論的東西都可以使用在這些資料庫之上)。
在這三個資料庫中,Oracle的連結速度一直都是令我非常頭大的,因為在這三者之中,Orcale連結資料庫和開啟資料庫表格的時間最久。
所以本節的範例就以Oracle資料庫為範例,看看使用了Package之後會不會有任何明顯的改善。
首先請你先建立一個Package,並且在這個Package之中產生一個資料模組,並且使用Database和Query元件連結到Oracle的資料庫如圖六所示。
圖六存在於Package之中的資料
模組使用Database連結到Oracle
在成功的編譯了這個Package之後,再讓我們設計範例程式的主表格如圖七一樣。
圖七使用公用資料模組的主表格
在主表格中有一個DataSource元件。
這個DataSource元件會在資料模組的Package載入之後,連結到資料模組之中的Query元件以便顯示Oracle資料庫之中的資料。
現在剩下的工作便是撰寫程式碼載入資料模組Package和顯示資料庫的資料。
首先在主表格啟動時,它必須在FormActivate事件處理函數中載入資料模組Package。
它呼叫LoaddbPackage這個程序。
procedureTMainForm.FormActivate(Sender:
TObject);
begin
LoaddbPackage;
end;
LoaddbPackage是真正負責載入Package的程序。
它在一個tryexcept程式區塊中呼叫Delphi的LoadPackge函數載入指定名稱的Package。
這個函數在成功執行後會回傳一個Package的handle值。
我們的程式必須儲存這個handle值以便稍後使用它,並且在最後藉由這個handle值釋放載入的Package。
如果呼叫LoadPackage成功的話,程式就呼叫LoadDataModule從Package中取得前面介紹的資料模組,否則在except程式區塊中ShowMessage會顯示發生錯誤的原因。
procedureTMainForm.LoaddbPackage;
begin
//我們必須載入資料庫Package以便連結到資料庫
try
aDBConnect:
=LoadPackage(DBPackages);
LoadDataModule;
except
onE:
Exceptiondo
begin
MessageBeep(Word(-1));
ShowMessage(E.Message);
Application.Terminate;
end;
end;
end;
在LoadDataModule中我們必須先從資料模組Package之中取得資料模組的真正Meta-Class名稱,然後使用這個Meta-Class建立真正的資料模組物件。
所以LoadDataModule一開始會呼叫GetClass向Windows取得特定類別名稱的Meta-Class。
而GetClass傳入的參數『TConcreteDataModule』,便是前面我們建立的資料模組的真正的類別名稱。
由於一個獨立的EXE要能夠取得Package之中的Meta-Class必須使用指定的類別名稱。
所以當你在Package中撰寫任何的類別時,你必須確定這個類別名稱在所有Delphi或是應用程式載入的Package中都是唯一的。
否則如果有在所有載入的Pakcage中有相同的類別名稱時,Delphi或是應用程式會產生一個例外。
在成功的從Package取得了Meta-Class之後,你就必須使用這個Meta-Class產生真正的資料模組物件。
請注意在下面的程式碼中,我們使用了強制型態轉換把TComponentClass的物件轉換為TDataModule的物件。
在建立了TDataModule物件之後,我們就可以一一的搜尋資料模組之中的元件並且找到我們需要的Query元件,並且把主表格中DataSource的DataSet特性值設定為找到的Query元件。
procedureTMainForm.LoadDataModule;
var
iCounter:
Integer;
begin
{NotethatTApplication"owns"thisformandthusitmustbefreedprior
tounloadingthepackage}
dataModuleClass:
=GetClass('TConcreteDataModule');
ifdataModuleClass<>nilthen
begin
admGlobal:
=
TDataModule(TComponentClass(dataModuleClass).Create(Application));
foriCounter:
=0toadmGlobal.ComponentCount-1do
begin
ifUpperCase(admGlobal.Components[iCounter].ClassName)='TQUERY'then
begin
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 如何 使用 Delphi 开发 大型 主从 架构 系统