VCLプロジェクトの近代化の目的で作成されたGOFコマンドパターンの簡素化されたバージョン。また、このプロジェクトにアクションファクトリを追加しました。これは、コマンドをVCLアクションに包み込んでいます。

このプロジェクトには、パターン実装の2つのバージョンが含まれています。
ICommandインターフェイスのクラシックギャングTCommandクラスTCommandコンポーネントは、レガシーVCLコードの近代化を支援するために作成されました。これは、単体テストで固定した後、オブジェクト指向のコードを維持するためにクリーンで安価にリファクタリングできるようにする、絡み合ったコードの抽出を支援します。
TCommandコンポーネントは、抽出されたコードをクリアした後およびUI依存関係を削除した後にリファクタリングする必要がある遷移オブジェクトです

TCommandコンポーネントを使用する最も簡単な方法は、新しいクラスを作成し、長い方法を貼り付けて、実行されたプロパティとしてすべての依存関係を追加することです。 Sample Bellowを参照してください。
VCLアプリケーションでのTCommand使用の図:

新しいコマンドを構築する開発者は、 TCommand (Unit: Pattern.Command.pas )から派生した新しいクラスを定義し、メインコマンドロジックを含む保護されたメソッドDoExecute実装する必要があります。
開発者は、 DoExecuteの前に呼び出され、すべての必須注射を検証できるメソッドDoGuardも実装できます(注入システムはBellowed)。通常、すべての注射はアサートコールでチェックされます。
注入なしのサンプルコマンド(空のガード):
type
TDiceRollCommand = class (TCommand)
protected
procedure DoExecute ; override;
end ;
procedure TDiceRollCommand.DoExecute ;
begin
ShowMessage( ' Dice roll: ' +RandomRange( 1 , 6 ).ToString);
end ;コマンドを実行するには、オブジェクトを作成し、 DoGuardをExecuteてからDoExecute呼び出す必要があります。
cmd := TDiceRollCommand.Ceate(Self);
cmd.Execute;TCommandコンポーネントは、IDEフォームデザイナー(Object Inspector)が使用する古典的なRTTIメカニズムに基づいて、自動インジェクションシステムを組み込みました。注射可能にさらされたプロパティは、コンポーネント(コマンド)のpublishedセクションで定義する必要があります。すべてのコンポーネントベースのクラスは、コンピレーションプロセス中にランタイムタイプの情報生成に切り替えられました(コンパイラオプション{$TYPEINFO ON} )。新しいコマンドの作成中に、すべての依存関係を簡単に提供し、公開されたプロパティに自動的に割り当てることができます。クラシックなRTTIエンジンの詳細については、Delphiのドキュメントで見つけることができます:ランタイムタイプ情報
2つの依存関係を持つサンプルコマンド(1つは必須、1つはオプション):
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アクションですコマンドに抽出されたビジネスロジックは、別の背景スレッドで処理される非同期コマンドに簡単に変換できます。 TCommandクラスをTAsyncCommandに置き換えることは、そのような変換において最初に急なです。
uses
Pattern.AsyncCommand;
type
TAsyncDiceRollCommand = class (TAsyncCommand)
...
end ;変更は非常に単純ですが、一般に、マルチスレッド処理ははるかに深刻なテーマであり、この領域のより深い知識が必要です。この例( TDiceRollCommand )では、2つのトピックに問題があります。
fProgressBar: TProgressBarfOutputRolls: TStringsそれらに簡単に対処できますが、これにはより一般的なマルチスレッド処理の知識が必要です。専用のドキュメントで見つけることができます:非同期コマンド
TCommandAction 、 TActionに基づいたラッパークラスであり、 TCommandクラスに基づいてコマンドを実行できます。開発者は、VCLアプリケーションを構築するときに、このアクションを多くのコントロール(アクションによって駆動される視覚コンポーネントまたはアクション認識)に簡単にバインドできます。たとえば、 TCheckBoxは、使用されるときに実行されるActionプロパティがありますチェックボックス状態(チェック)が変更されます。アクションには、通知システムのビルドなど、そのような2つのエンジンのような他の利点があります。1つは視覚状態を更新するためのエンジンと、既存のコンポーネントの新規および削除の作成について通知するためのより内部です。どちらのエンジンも複雑すぎてこのセクションで説明できません。詳細については、Delphiオンラインドキュメントに記載されています。
形式のアーキテクチャの視点TCommandAction 、招待者オブジェクトとして使用でき、移行後により弾力性のあるカスタムソリューションに置き換えることができます。
TCommandAction Invokerのサンプル構造:
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) | アクションOnUpdateイベントの後にトリガーされたイベント |
WithEventAfterExecution(aProc) | コマンドが完了したときにトリガーされたイベント |
TCommandActionでのサンプルセットアップOnUpDateイベント:
Button2.Action := TCommandAction.Create(Self)
.WithCaption( ' Run sample command ' )
.WithCommand(MySampleCommand)
.WithEventOnUpdate(
procedure(cmd: TCommandAction)
begin
cmd.Enabled := CheckBox1.Checked;
end );TCommandパターンにより、開発者は貴重なビジネスコードを抽出し、アプリケーションをより少なくすることができます。同時に、開発者は依然としてよく知られているコンポーネントプラクティスを使用し、コマンドコンポーネントを使用してより複雑なコードを作成できます。開発者は、独自のプロパティやイベントでコマンドパターンを拡張することもできます。ただし、このアプローチは一時的なソリューションであり、よりオブジェクト指向のデザインに進化する必要があります。
Tcommandパターンは、GOFコマンドパターン(上記の図を参照)と互換性があり、近代化できます。このモデレーションは、リファクタリングフェーズが終了し、ロジックが単体テストでカバーされるときに開始する必要があります。リファクタリング中にすべての視覚依存関係を削除する必要があります。また、すべての無関係な依存関係も削除され、コードはより小さな論理的な方法またはクラスに分解する必要があります。
近代化後、すべての依存関係をコンストラクターを介して挿入する必要があります。コマンドはインターフェイスを介してアクセスする必要があります。コマンド内部アイテムへのアクセスは、ゲッターおよびセッターメソッドを介して行う必要があります。構成されたオブジェクトは、Spring4D GlobalContainerメソッドなどのDIコンテナを使用して作成する必要があります。
アドホックコマンド実行(作成、注入、実行、削除)
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 ;