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 ;