Vereinfachte Version des GOF -Befehlsmusters, das zum Zweck der Modernisierung von VCL -Projekten erstellt wurde. Auch Action Factory zu diesem Projekt hinzugefügt, in dem ein Befehl in die VCL -Aktion einfließt.

Das Projekt enthält zwei Versionen der Musterimplementierung:
ICommand -SchnittstellenTCommand -Klasse basierend auf TComponent Die TCommand -Komponente wurde erstellt, um die Modernisierung des Legacy VCL -Codes zu verbessern. Es hilft die Extraktion des verworrenen Code, der nach der Sicherung durch Einheitstests in sauberer und billiger umgeführt werden kann, um den objektorientierten Code zu pflegen.
TCommand -Komponente ist ein Übergangsobjekt, das nach dem Löschen extrahierter Code und nach Entfernen von UI -Abhängigkeiten neu ausgerichtet werden sollte

Der einfachste Weg, die TCommand -Komponente zu verwenden, besteht darin, eine neue Klasse zu erstellen, eine lange Methode in die Ausführungsmethode einzufügen und alle Abhängigkeiten als veröffentlichte Eigenschaften hinzuzufügen. Siehe Probe unten.
Diagramm der TCOMMAND -Verwendung in der VCL -Anwendung:

Der Entwickler zum Erstellen eines neuen Befehls muss neue Klasse definieren, die von TCommand (Einheit: Pattern.Command.pas ) abgeleitet wurden, und implementiert eine geschützte Methode DoExecute , die eine Hauptbefehlslogik enthält.
Der Entwickler kann auch eine Methode implementieren, die DoGuard vorliegt, die vor DoExecute aufgerufen wird und alle Abitur -Injektionen überprüfen kann (Injektionssystem wird erklärt). Normalerweise werden alle Injektionen mit Assert Call überprüft.
Beispielbefehl ohne Injektion (leere Wache):
type
TDiceRollCommand = class (TCommand)
protected
procedure DoExecute ; override;
end ;
procedure TDiceRollCommand.DoExecute ;
begin
ShowMessage( ' Dice roll: ' +RandomRange( 1 , 6 ).ToString);
end ; Um den Befehl auszuführen, sollten Sie Objekt erstellen und anrufen, um die öffentliche Methode Execute , die DoGuard nennt und dann DoExecute :
cmd := TDiceRollCommand.Ceate(Self);
cmd.Execute; TCommand -Komponente hat ein automatisiertes Injektionssystem basierend auf dem klassischen RTTI -Mechanismus, der von IDE Form Designer (Objektinspektor) verwendet wird. Eigenschaften, die als injizierbar ausgesetzt sind, müssen im published Abschnitt der Komponente (Befehl) definiert werden. Alle komponentenbasierten Klassen haben während des Kompilierungsprozesses die Informationsgenerierung von Laufzeittypen eingeschaltet (Compiler-Option {$TYPEINFO ON} ). Dank dessen bei der Erstellung eines neuen Befehls können alle Abhängigkeiten leicht zur Verfügung gestellt und den veröffentlichten Eigenschaften automatisch zugewiesen werden. Weitere Informationen zu klassischer RTTI-Engine finden Sie in Delphi-Dokumentation: Laufzeitinformationen
Beispielbefehl mit zwei Abhängigkeiten (einer und einer optional):
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 ;Die verfügbaren veröffentlichten Eigenschaften von TCOMMAND werden mit den in Parametern übergebenen Parametern (Open Array) übereinstimmen. Die folgenden Regeln werden durch Übereinstimmung mit Algorithmus verwendet:
Warnung! Injiziertes Objekt wird durch die Adresse im Speicher (Zeiger) zugegriffen, dank der Änderungen an Objekten sind innerhalb und außerhalb des TCommand sichtbar. Auf einfache Typen und Zeichenfolgen werden über Wert zugegriffen, und Eigenschaften müssen manuell aktualisiert werden, um aktualisiert zu werden.
Beispielcode injizieren Objekte in Eigenschaften von TDICerollCommand:
cmd := TDiceRollCommand.Create(Self)
.Inject([Memo1.Lines,ProgressBar1]);Die beliebteste und normalerweise empfohlene Methode zur Injektion von Abhängigkeiten ist eine Konstruktorinjektion. Diese hier eingeführte Lösung (TCommand -Muster) ist mehr Komponentenbasis. Dieses Muster ähnelt eher einer Übergangsphase, die es ermöglicht, wichtige Teile der großen Anwendung schnell zu extrahieren und auszuführen. Der endgültige Zielpunkt in diesem Prozess ist die beste architektonische Lösung, bedeutet die Injektion durch den Konstruktor und die Verwendung von Schnittstellen anstelle von Objekten.
TCommand.AdhocExecute<T> - führt einen Befehl aus (erstellt einen Befehl, injiziert Abhängigkeiten führen ihn aus und entfernt).InjectExecute ausführenTCommandAction der den Befehl ausführt, wenn die Aktion aufgerufen wirdTCommandAction Class ist klassische VCL -Aktion Die in den Befehl extrahierte Geschäftslogik kann leicht in einen asynchronen Befehl umgewandelt werden, der in einem separaten Hintergrund -Thread verarbeitet wird. Das Ersetzen TCommand -Klasse durch TAsyncCommand ist zunächst in einer solchen Transformation steil:
uses
Pattern.AsyncCommand;
type
TAsyncDiceRollCommand = class (TAsyncCommand)
...
end ; Obwohl die Veränderung sehr einfach ist, aber im Allgemeinen ist die Verarbeitung von Multi-Threaden ein viel schwerwiegenderes Thema und erfordert tiefere Kenntnisse über diesen Bereich. In diesem Beispiel ( TDiceRollCommand ) sind zwei Themen problematisch:
fProgressBar: TProgressBarfOutputRolls: TStringsSie können leicht mit ihnen umgehen, dies erfordert jedoch allgemeineres Wissen zur Multithread -Verarbeitung. Weitere Informationen finden Sie in der dedizierten Dokumentation: Asynchroner Befehl
TCommandAction ist eine auf TAction basierende Wrapper -Klasse und kann Befehle basierend auf TCommand -Klasse ausführen. Der Entwickler kann beim Erstellen von VCL-Anwendung diese Aktion problemlos an viele Steuerelemente binden (visuelle Komponenten, die durch Aktionen gesteuert werden oder Aktionen sind). Beispielsweise hat TCheckBox Action , die bei der Verwendung ausgeführt wird, wenn die Verwendung des Kontrollkästchen Status geändert wird (geprüft). Aktionen haben einige andere Vorteile wie Build im Benachrichtigungssystem, genau zwei solcher Motoren: eine zum Aktualisieren des visuellen Zustands und eines anderen, internen, um die Erstellung neuer und Löschungen vorhandener Komponenten zu melden. Beide Motoren sind zu komplex, um in diesem Abschnitt beschrieben zu werden. Weitere Informationen finden Sie in der Delphi Online -Dokumentation.
Das Aussehen der architektonischen Perspektive von Form TCommandAction
Beispielkonstruktion auf TCommandAction Invoker:
Button1.Action := TCommandAction.Create(Button1)
.WithCaption( ' Run sample command ' )
.WithCommand(TSampleCommand.Create(Button1))
.WithInjections([Memo1, Edit1]);| Versorgungsmethode | Beschreibung |
|---|---|
WithCaption(aCaption) | Legt eine Aktionsunterschrift fest, die in einer Kontrolle angezeigt wird |
WithShortCut(aShortcut) | Legt eine Verknüpfung fest, die eine Aktion aktiviert |
WithCommand(aCommand) | Legt einen Befehl zur Ausführung fest |
WithInjections(aInjections) | Injiziert Werte in die Eigenschaften des Befehls |
WithEventOnUpdate(aProc) | Ereignis ausgelöst nach Action Onupdate -Ereignis |
WithEventAfterExecution(aProc) | Ereignis ausgelöst, wenn der Befehl fertig ist |
Beispiel -Setup -Onupdate -Ereignis in TCommandAction :
Button2.Action := TCommandAction.Create(Self)
.WithCaption( ' Run sample command ' )
.WithCommand(MySampleCommand)
.WithEventOnUpdate(
procedure(cmd: TCommandAction)
begin
cmd.Enabled := CheckBox1.Checked;
end );Mit TCommand Muster können Entwickler die wertvolle Geschäftsordnung extrahieren und Anwendungen weniger gekoppelt machen. Gleichzeitig können Entwickler weiterhin bekannte Komponentenpraktiken verwenden und komplexere Code mit Befehlskomponenten komponieren. Entwickler können sogar das Befehlsmuster mit ihren eigenen Eigenschaften und Ereignissen erweitern. Dieser Ansatz ist jedoch eine vorübergehende Lösung und sollte zu einem objektorientierten Design entwickelt werden.
Das TCommand -Muster ist mit dem GOF -Befehlsmuster kompatibel (siehe Diagramme oben) und kann modernisiert werden. Diese Mäßigung sollte gestartet werden, wenn die Refactoring -Phase fertig ist und die Logik durch Unit -Tests abgedeckt wird. Während des Refactorings sollten alle visuellen Abhängigkeiten entfernt werden, auch alle irrelevanten Abhängigkeiten und der Code sollte in kleinere logischere Methoden oder Klassen zusammenbrechen.
Nach der Modernisierung sollten auf alle Abhängigkeiten über den Konstruktor eingespritzt werden. Auf den Befehl sollte über die Schnittstelle zugegriffen werden. Der Zugriff auf interne Elemente auf Befehl sollte über Getter- und Setter -Methoden erfolgen. Komponierte Objekte sollten mit dem DI -Container erstellt werden, wie wie die Spring4D GlobalContainer -Methode.
Ad-hoc-Befehlsausführung (erstellen, injizieren, ausführen, entfernen)
TCommand.AdhocExecute<TSampleCommand>([Memo1, Edit1]);Erstellt Befehl und injizieren Abhängigkeiten:
cmdSampleCommand := TSampleCommand.Create(AOwner);
cmdSampleCommand.Inject([Memo1, Edit1]); Beispiel TCommand -Komponente:
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 ;