GOF命令模式的簡化版本,為VCL項目的現代化而創建。此項目還添加了動作工廠,該項目將命令包裝到VCL操作中。

該項目包含兩個模式實現的版本:
ICommand界面的幫派TCommand類創建TCommand組件是為了幫助舊版VCL代碼的現代化。它有助於提取糾結的代碼(用單元測試將其固定後)可以重新製作為更清潔,更便宜,以維護面向對象的代碼。
TCommand組件是一個過渡對象,在清除提取的代碼和刪除UI依賴項後應進行重構

使用TCommand組件的最簡單方法是將新的類,粘貼長的方法創建到執行方法中,並將所有依賴項添加為已發布的屬性。請參閱樣品bellow。
VCL應用中的TCommand使用圖:

開發人員要構建新命令需要定義從TCommand (單位: Pattern.Command.pas )派生的新類,並實現一個受保護的方法DoExecute ,其中包含主要命令邏輯。
開發人員還可以實現一種方法,該DoGuard在DoExecute之前被調用,並允許驗證所有強制注射(解釋了注射系統)。通常,所有註射都會通過斷言呼叫檢查。
沒有註射的樣本命令(空後衛):
type
TDiceRollCommand = class (TCommand)
protected
procedure DoExecute ; override;
end ;
procedure TDiceRollCommand.DoExecute ;
begin
ShowMessage( ' Dice roll: ' +RandomRange( 1 , 6 ).ToString);
end ;要執行命令,您應該創建對象並調用Execute public方法,該方法呼叫DoGuard ,然後DoExecute :
cmd := TDiceRollCommand.Ceate(Self);
cmd.Execute;TCommand組件已基於IDE FOM DESICETER(Object Inspector)使用的經典RTTI機制內置自動注入系統。必須在組件(命令)的published部分中定義暴露於注射的屬性。所有基於組件的類都均已打開編譯過程中運行時類型信息的生成(編譯器選項{$TYPEINFO ON} )。在創建新命令期間,所有依賴關係都可以自動提供並分配給已發布的屬性。有關經典RTTI引擎的更多信息,可以在Delphi文檔中找到:運行時類型信息
帶有兩個依賴項的示例命令(一個必需和一個可選):
type
TDiceRollCommand = class (TCommand)
const
RollCount = 100 ;
private
fOutput: TStrings;
fProgressBar: TProgressBar;
procedure ShowProgress (aRoll: integer);
protected
procedure DoGuard ; override;
procedure DoExecute ; override;
published
property OutputRolls: TStrings read fOutput
write fOutput;
property ProgressBar: TProgressBar read fProgressBar
write fProgressBar;
end ;
procedure TDiceRollCommand.DoGuard ;
begin
System.Assert(fOutput<> nil );
end ;
procedure TDiceRollCommand.ShowProgress (aRoll: integer);
begin
if Assigned(fProgressBar) then begin
if aRoll= 0 then
fProgressBar.Max := RollCount;
fProgressBar.Position := aRoll;
end ;
end
procedure TDiceRollCommand.DoExecute ;
begin
ShowProgress( 0 );
for var i := 0 to RollCount- 1 do
begin
fOutput.Add(RandomRange( 1 , 7 ).ToString);
ShowProgress(i+ 1 );
end ;
end ;可用的TCommand的可用屬性與參數中傳遞的參數類型匹配(開放陣列)。匹配算法使用以下規則:
警告!注入的對像是通過存儲器(指針)中的地址訪問的,這要歸功於對對象所做的任何更改在TCommand的內部和外部可見。簡單的類型和字符串是通過值訪問的,屬性必須手動更新以進行更新。
示例代碼將對象注入tdicerollCommand的屬性:
cmd := TDiceRollCommand.Create(Self)
.Inject([Memo1.Lines,ProgressBar1]);注射依賴性的最流行和通常建議的方法是構造函數注入。此處介紹的解決方案(TCommand模式)是基於組件的方法。這種模式更像是一個過渡階段,它可以快速提取並執行大型應用程序的重要部分。該過程中的最終目標點是最佳的架構解決方案,是指通過構造函數注入並使用接口而不是對象。
TCommand.AdhocExecute<T> - 執行命令(創建命令,注入依賴項執行並刪除)InjectExecute命令TCommandAction ,在調用操作時執行命令TCommandAction類是經典的VCL動作提取到命令中的業務邏輯可以輕鬆地轉換為異步命令,該命令在單獨的背景線程中處理。用TAsyncCommand代替TCommand類是這種轉變的首先是陡峭的:
uses
Pattern.AsyncCommand;
type
TAsyncDiceRollCommand = class (TAsyncCommand)
...
end ;儘管更改非常簡單,但是通常,多線程處理是一個更為嚴肅的主題,需要更深入的了解該領域。在此示例中( TDiceRollCommand )兩個主題是有問題的:
fProgressBar: TProgressBarfOutputRolls: TStrings您可以輕鬆地處理它們,但這需要更多一般的多線程處理知識。您可以在專用文檔中找到更多信息:異步命令
TCommandAction是基於TAction的包裝類別類,並且能夠基於TCommand類執行命令。開發人員在構建VCL應用程序時可以輕鬆地將此動作綁定到許多控件(由動作驅動或具有動作感知的視覺組件)。例如, TCheckBox具有Action屬性,在使用時將執行該屬性(檢查)。操作還有其他一些優點,例如在通知系統中構建,正是兩個這樣的引擎:一種用於更新視覺狀態,另一個用於內部的引擎,用於通知創建現有組件的新和刪除。這兩種引擎都太複雜了,無法在本節中描述,可以在Delphi在線文檔中找到更多信息。
看起來形式的架構TCommandAction可以用作調用對象,並且在遷移後可以用更彈性的自定義解決方案代替。
TCommandAction調用者的樣本構造:
Button1.Action := TCommandAction.Create(Button1)
.WithCaption( ' Run sample command ' )
.WithCommand(TSampleCommand.Create(Button1))
.WithInjections([Memo1, Edit1]);| 實用方法 | 描述 |
|---|---|
WithCaption(aCaption) | 設置一個動作標題,該字幕顯示在控件中 |
WithShortCut(aShortcut) | 設置一個正在激活動作的快捷方式 |
WithCommand(aCommand) | 設置命令執行 |
WithInjections(aInjections) | 將值注入命令的屬性 |
WithEventOnUpdate(aProc) | 在操作後觸發事件事件觸發事件 |
WithEventAfterExecution(aProc) | 當命令完成時觸發事件 |
TCommandAction中的示例設置update事件:
Button2.Action := TCommandAction.Create(Self)
.WithCaption( ' Run sample command ' )
.WithCommand(MySampleCommand)
.WithEventOnUpdate(
procedure(cmd: TCommandAction)
begin
cmd.Enabled := CheckBox1.Checked;
end );TCommand模式使開發人員可以提取有價值的業務代碼,並使應用程序不再耦合。同時,開發人員仍然可以使用眾所周知的組件實踐,並使用命令組件構成更複雜的代碼。開發人員甚至可以通過自己的屬性和事件來擴展命令模式。但是,這種方法是一種臨時解決方案,應演變為更面向對象的設計。
TCommand模式與GOF命令模式兼容(請參見上圖),並且可以現代化。當重構階段將完成並通過單位測試涵蓋邏輯時,應開始這種適度。在重構過程中,應刪除所有視覺依賴性,還應將所有無關的依賴性依賴,並且代碼應分解為較小的更邏輯的方法或類。
在現代化之後,應通過構造器注入所有依賴項,應通過接口訪問命令,對命令內部項目的訪問應通過getter和setter方法。應使用DI容器(例如Spring4D GlobalContainer方法)創建組成對象。
臨時命令執行(創建,注入,執行,刪除)
TCommand.AdhocExecute<TSampleCommand>([Memo1, Edit1]);創建命令和注入依賴性:
cmdSampleCommand := TSampleCommand.Create(AOwner);
cmdSampleCommand.Inject([Memo1, Edit1]);樣本TCommand組件:
type
TSampleCommand = class (TCommand)
private
FMemo: TMemo;
FEdit: TEdit;
protected
procedure DoGuard ; override;
procedure DoExecute ; override;
published
property Memo: TMemo read FMemo write FMemo;
property Edit: TEdit read FEdit write FEdit;
end ;
procedure TSampleCommand.DoGuard ;
begin
System.Assert(Memo<> nil );
System.Assert(Edit<> nil );
end ;
procedure TSampleCommand.DoExecute ;
begin
Memo.Lines.Add( ' Getting Edit text and put it here ... ' );
Memo.Lines.Add( ' * Edit.Text: ' +Edit.Text);
end ;