標籤:
1、 初步體驗
我們先看一個架構內建的例子,以增加感性認識。開啟samples\singleDEMO樣本項目。這個樣本示範了在一個EXE程式內,使用外掛程式的概念調用兩個視窗。其中包括一個主表單 ufrmMain.pas檔案,2 個作為外掛程式的子表單檔案(Child目錄),三個介面檔案(Interface目錄)。
項目結構如:
我們先看主表單
主表單的主要代碼:
procedure TfrmMain.btnSingletonFormClick(Sender: TObject); //建立一個單一實例表單
begin
with TMyBeanFactoryTools.getBean(‘singletonDEMO‘) as IShowAsNormal do
begin
showAsNormal;
end;
end;
procedure TfrmMain.Button1Click(Sender: TObject); //建立一個Bean表單
begin
with TMyBeanFactoryTools.getBean(‘tester‘) as IUIForm do
try
showAsModal;
finally
UIFormFree;
end;
end;
注意上面紅色代碼部分,都有一個getBean方法,帶一個字串參數表示外掛程式名稱,調用後返回外掛程式對象,然後通過as 操作,轉換為對象支援的介面類型(對介面不熟悉的朋友請參閱相關知識)。上面這個getBean方法是TMyBeanFactoryTools 類的一個類方法。
TMyBeanFactoryTools 類本身定義在mybean.tools.beanFactory單元中,所以主表單需要在引用列表中加入這個單元。
主表單調用介面非常簡單,通過調用TMyBeanFactoryTools的getBean方法,傳入外掛程式名稱即可返回外掛程式對象,並訪問外掛程式的方法。
這種以“外掛程式”的方式調用子表單,可以看到主視窗並沒有引用(uses)子視窗單元檔案。也就是說主表單與子表單實現了“解藕”。
那麼,getBean方法為什麼能通過名字找到外掛程式呢?按照猜測,子表單應該通過什麼方法向架構系統進行了某種形式的“註冊登記”,這樣主表單才能尋找到。
所以,我們接著看子表單的實現。我們開啟其中的ufrmSingleton單元,它的表單介面如下:
再查看它的代碼:
type
TfrmSingleton = class(TForm, IFreeObject, IShowAsNormal)
Memo1: TMemo;
private
{ Private declarations }
public
{ Public declarations }
procedure FreeObject; stdcall;
procedure showAsNormal; stdcall;
end;
看到現在,我們還沒有發現子表單究竟做了什麼“註冊登記”的操作。不要急,在代碼最後,我們發現了initialization段的代碼,這段代碼在單元剛載入時初始化。那麼這裡它做了什麼呢?
initialization
beanFactory.RegisterBean(‘singletonDEMO‘, TfrmSingleton);
beanFactory.configBeanSingleton(‘singletonDEMO‘, true);
我們發現有一個 beanFactory 對象(實際是一個方法function beanFactory: TBeanFactory;),顧名思義,應該是一個bean工廠,專門生產bean(這裡也就是我們要做的外掛程式)。這個工廠類有一個RegisterBean方法,把外掛程式註冊到某個“登記簿”中去。而‘singletonDEMO‘就是外掛程式登記的名字,TfrmSingleton是外掛程式的類型。這就驗證了我們的猜測。
後面的beanFactory.configBeanSingleton(‘singletonDEMO‘, true),則是指明這個外掛程式是個單例模式的外掛程式,即只能建立一個對象執行個體。
那麼,這個beanFactory類型又是哪裡聲明的呢?查看一下,在 mybean.core.beanFactory;單元中。所以這個子表單的uses列表中也有這個單元的名稱。
我們暫且不去探究beanFactory的內部是如何工作的,先只要知道它在單元初始化部分登記了外掛程式的類名稱,把它登記到了架構核心內部的一份“登記簿”中去。然後主程式表單通過TMyBeanFactoryTools.getBean (外掛程式名稱) 方法調用,通過尋找內部“登記簿”,獲得外掛程式的類別,並建立類的執行個體,轉換成約定的介面。這就是架構工作的大致流程了。
主程式端 Uses mybean.tools.beanFactory; TMyBeanFactoryTools.getBean擷取外掛程式執行個體 |
外掛程式端 Uses mybean.core.beanFactory beanFactory.RegisterBean() 註冊外掛程式類 |
當然,作為外掛程式的子表單,也應該有“與從不同”的自覺。因為它肯定要比普通的表單對象承擔一些額外的功能。當然,作為架構使用者來說,這種“不同”之處當然是越少越好,這樣使用myBean架構才會不那麼繁瑣。
那麼,作為myBean外掛程式對象的表單,與普通表單的不同處到底在哪裡呢?要怎麼做才能成為一個外掛程式呢?
看TfrmSingleton 的定義:
TfrmSingleton = class(TForm, IFreeObject, IShowAsNormal)
這個表單,在普通TFORM基礎上,實現了兩個介面IFreeObject和IShowAsNormal,這兩個介面分別定義了FreeObject方法和showAsNormal方法。那麼IFreeObject和IShowAsNormal本身定義在哪裡呢?
通過尋找,我們發現 IFreeObject定義在mybean.core.intf單元中,這是架構提供的核心單元,暫不去管它。
而IShowAsNormal定義在uIFormShow單元中:
IShowAsNormal = interface(IInterface)
[‘{4A2274AB-3069-4A57-879F-BA3B3D15097D}‘]
procedure showAsNormal; stdcall;
end;
(因為引用了這兩個介面,所以不要忘了uses mybean.core.intf和uIFormShow單元)
而FreeObject方法和showAsNormal方法的實現都很普通。freeObject就是調用self.Free 把對象自身free掉。而showAsNormal就是一個最普通不過的show()。
再回到主視窗的代碼,看它是如何調用這個子表單的:
with TMyBeanFactoryTools.getBean(‘singletonDEMO‘) as IShowAsNormal do
begin
showAsNormal;
end;
我們發現,‘singletonDEMO‘正是子視窗向架構註冊時用的名字,而IShowAsNormal 正是子視窗實現的介面之一。主視窗中也引用了這個介面檔案,所以可以通過這個介面調用方法,而不管子表單究竟是什麼類型的對象。
而實際上,myBean並不強制要求子視窗一定要實現某個特定的介面,你完全可以隨便設定子視窗要實現的介面。唯一的約定,就是主視窗和子視窗(作為外掛程式)之間都要遵循同一套介面,以便主視窗在通過GetBean獲得子視窗對象後,能夠轉型為約定的介面並調用介面定義的方法。myBean是通過介面實現主表單與外掛程式之間溝通的。
當然,為了開發的便利,架構約定了一個IFreeObject介面。如果外掛程式實現了這個介面,則它就可以自己管理生存期,而不需要程式員手動去銷毀。
在這個樣本中,有兩個子表單,其中一個TfrmSingleton實現了IFreeObject介面,另一個TfrmTester則沒有實現這個介面,在使用後需要程式員手動釋放,見主表單的代碼:
with TMyBeanFactoryTools.getBean(‘tester‘) as IUIForm do
try
showAsModal;
finally
UIFormFree; //手動釋(銷毀)外掛程式對象
end;
這個UIFormFree方法也是IUIForm 介面中定義的,在TfrmTester中實現了這個介面方法:
procedure TfrmTester.UIFormFree;
begin
self.Free;
end;
上面囉囉嗦嗦分析了這麼多,其實總結起來就是以下內容:
主表單端
① 引用mybean.tools.beanFactory (定義TMyBeanFactoryTools)
② 調用TMyBeanFactoryTools.getBean方法擷取外掛程式對象
外掛程式端:
① 引用:mybean.core.beanFactory (beanFactory類的RegisterBean,configBeanSingleton方法),mybean.core.intf (定義FreeObject介面)
② 2、調用:beanFactory.RegisterBean方法註冊外掛程式,beanFactory.configBeanSingleton方法配置配件資訊。
2、進一步探索
看完了幾個表單的代碼,現在我們再來查看這個項目的原始碼,看使用myBean架構還需要做些什麼準備工作。
program singleDEMO;
uses
Forms,
mybean.core.beanFactory,
mybean.console,
ufrmMain in ‘ufrmMain.pas‘ {frmMain},
ufrmTester in ‘Child\ufrmTester.pas‘ {frmTester},
uIUIForm in ‘Interface\uIUIForm.pas‘,
ufrmSingleton in ‘Child\ufrmSingleton.pas‘ {frmSingleton},
uIShow in ‘Interface\uIShow.pas‘,
uIFormShow in ‘Interface\uIFormShow.pas‘;
{R *.res}
begin
Application.Initialize;
registerFactoryObject(beanFactory, ‘default‘);
Application.MainFormOnTaskbar := True;
Application.CreateForm(TfrmMain, frmMain);
Application.Run;
end.
注意上面紅色部分代碼,首先它引用了 mybean.core.beanFactory和 mybean.console兩個核心單元。然後在代碼執行部分註冊了一個工廠類的執行個體:registerFactoryObject(beanFactory, ‘default‘)。
通過上述單元引用,架構運行所需要的環境就建立了。
v 本章小結:
要使用myBean架構,需要做以下幾個步驟:
l 主程式端
1、在主程式專案檔(.dpr檔案)中引用 mybean.console (提供外掛程式架構環境);
2、在主程式專案檔(.dpr檔案)的begin end 部分,添加applicationContextInitialize命令,初始化架構執行環境,載入必要的外掛程式工廠(如果沒有找到設定檔,將自動載入程式所在目錄下的DLL外掛程式和plugin子目錄下的bpl外掛程式);
3、在需要引用外掛程式的單元檔案開頭,引用mybean.tools.beanFactory單元,並用TMyBeanFactoryTools.getBean(‘TestDll‘) as Ixxxxxx (Ixxxxxx 為外掛程式與主程式共同約定的介面)的形式調用外掛程式。
l 外掛程式端(以DLL為例):
1、建立DLL項目,在專案檔中引用 uses mybean.core.beanFactory單元,以提供註冊外掛程式所需的工廠類;
2、在專案檔的begin .... end 段內,以beanFactory.RegisterBean(‘beanIDxxx‘,TBeanClassxxx)的方式註冊外掛程式。其中TBeanClassxxx可以是DLL中定義的任意類別識別項(包括表單),‘beanIDxxx‘是自己登記這個類時用的唯一識別碼號;
3、上述第1-2步註冊外掛程式的過程也可以分散在DLL的各單元的 initialization 段。相關單元需要引用 mybean.core.beanFactory單元;
4、註冊的外掛程式要實現與主程式共同約定的介面,以供主程式調用。
3、延伸閱讀(可選,不影響對架構的使用)
我們先分析mybean.console單元的作用。既然它在uses後就起了作用,說明它在initialization 段裡執行了一些東東,所以我們先去看這裡。
Initialization
{建立一個記錄作業記錄的TSafeLogger類型對象,並儲存到__beanLogger全域變數中}
__beanLogger := TSafeLogger.Create;
__beanLogger.setAppender(TLogFileAppender.Create(False));
__beanLogger.start;
{建立一個TKeyMapImpl類型的對象,儲存到 __instanceKeyMap}
__instanceKeyMap := TKeyMapImpl.Create;
__instanceKeyMapKeyIntf := __instanceKeyMap;
{主程式執行個體的上下文環境對象}
__instanceAppContext := TApplicationContext.Create;
{轉化成介面}
__instanceAppContextAppContextIntf := __instanceAppContext;
mybean.core.intf.appPluginContext := __instanceAppContext;
mybean.core.intf.applicationKeyMap := __instanceKeyMap;
appPluginContext.checkInitialize;
上面代碼的最後一行,是執行checkInitialize ,我們繼續跟蹤它到底幹了啥:
procedure TApplicationContext.checkInitialize;
var
lvConfigFiles:String;
begin
if FFactoryObjectList.Count = 0 then
begin
checkReady;
lvConfigFiles := FINIFile.ReadString(‘main‘, ‘beanConfigFiles‘, ‘‘);
if lvConfigFiles <> ‘‘ then
begin
if FTraceLoadFile then
__beanLogger.logMessage(‘從設定檔中載入bean配置‘, ‘LOAD_TRACE_‘);
if checkInitializeFromConfigFiles(lvConfigFiles) > 0 then
begin
if FINIFile.ReadBool(‘main‘, ‘loadOnStartup‘, False) then
begin
//載入DLL檔案, 把DLL載入
checkInitializeFactoryObjects;
end;
end else
begin
if FTraceLoadFile then
__beanLogger.logMessage(‘沒有載入任何設定檔‘, ‘LOAD_TRACE_‘);
end;
end else
begin
if FTraceLoadFile then
__beanLogger.logMessage(‘直接載入DLL檔案‘, ‘LOAD_TRACE_‘);
executeLoadLibrary;
end;
end;
end;
外掛程式設定檔命名:主程式名+‘.config.ini‘ 或 app.config.ini 。
如果存在設定檔,則FTraceLoadFile := True,否則FTraceLoadFile :=False;
1.3 官方資源
MyBean 由 D10.天地弦(QQ:185511468)開發。
官方Blog: http://www.cnblogs.com/DKSoft/
官方網站: www.diocp.org
討論QQ群: 205486036 (MyBean輕量級組態架構)
MyBean的源碼庫: https://git.oschina.net/ymofen/delphi-framework-MyBean
轉:認識MyBean