Version simplifiée du modèle de commande GOF, créé à des fins de modernisation des projets VCL. Ajout de l'usine d'action à ce projet, qui enveloppe une commande dans l'action VCL.

Le projet contient deux versions de la mise en œuvre du modèle:
ICommandTCommand basée sur TComponent Le composant TCommand a été créé pour aider la modernisation du code VCL hérité . Il aide à l'extraction du code enchevêtré, qui après l'avoir sécurisé avec des tests unitaires, peut être refactorisé en nettoyage et moins cher pour maintenir le code orienté objet.
Le composant TCommand est un objet de transition qui devrait être refactorisé après avoir effacé le code extrait et après la suppression des dépendances de l'interface utilisateur

La façon la plus simple d'utiliser le composant TCommand est de créer une nouvelle classe, de coller la méthode longue dans l'exécution de la méthode et d'ajouter toutes les dépendances en tant que propriétés publiées. Voir échantillon ci-dessous.
Diagramme de l'utilisation de TCommand dans l'application VCL:

Le développeur pour construire une nouvelle commande doit définir une nouvelle classe dérivée de TCommand (Unit: Pattern.Command.pas ) et implémente une méthode protégée DoExecute , qui contient une logique de commande principale.
Le développeur peut également implémenter une méthode DoGuard , qui est appelée avant DoExecute et permettre de vérifier toutes les injections obligatoires (le système d'injection est expliqué ci-dessous). Habituellement, toutes les injections sont vérifiées avec un appel d'affirmation.
Exemple de commande sans injection (garde vide):
type
TDiceRollCommand = class (TCommand)
protected
procedure DoExecute ; override;
end ;
procedure TDiceRollCommand.DoExecute ;
begin
ShowMessage( ' Dice roll: ' +RandomRange( 1 , 6 ).ToString);
end ; Pour exécuter la commande, vous devez créer un objet et appeler la méthode publique Execute , qui appelle DoGuard puis DoExecute :
cmd := TDiceRollCommand.Ceate(Self);
cmd.Execute; Le composant TCommand a intégré un système d'injection automatisé basé sur le mécanisme RTTI classique utilisé par IDE Form Designer (Inspecteur d'objets). Les propriétés exposées à être injectables doivent être définies dans la section published du composant (commande). Toutes les classes basées sur les composants ont activé la génération d'informations de type d'exécution pendant le processus de compilation (option de compilateur {$TYPEINFO ON} ). Merci lors de la création de nouvelles commandes, toutes les dépendances peuvent être facilement fournies et affectées automatiquement aux propriétés publiées. Plus d'informations sur le moteur RTTI classique peuvent être trouvées dans Delphi Documentation: Informations sur le type d'exécution
Exemple de commande avec deux dépendances (une requise et une facultative):
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 ;Les propriétés publiées disponibles de TCommand sont adaptées aux types de paramètres transmis dans les paramètres (tableau ouvert). Les règles suivantes sont utilisées par algorithme correspondant:
Avertissement! L'objet injecté est accessible par adresse en mémoire (pointeur), grâce à que toutes les modifications apportées à l'objet soient visibles à l'intérieur et à l'extérieur du TCommand. Les types et chaînes simples sont accessibles via la valeur et les propriétés doivent être mises à jour manuellement pour être mises à jour.
Exemple de code Injectant des objets aux propriétés de TdicerollCommand:
cmd := TDiceRollCommand.Create(Self)
.Inject([Memo1.Lines,ProgressBar1]);La méthode la plus populaire et généralement avisée d'injection de dépendances est une injection de constructeur. Cette solution introduite ici (modèle TCommand) est une approche plus basée sur des composants. Ce modèle ressemble plus à une étape de transition qui permet d'extraire et d'exécuter rapidement des parties importantes de la grande application. Le point cible final de ce processus est la meilleure solution architecturale, signifie l'injection via le constructeur et utilise des interfaces au lieu d'objets.
TCommand.AdhocExecute<T> - Exécute une commande (crée une commande, Injects Dependances l'exécute et supprime)Inject la méthode d'appelExecuteTCommandAction qui exécute la commande lorsque l'action est invoquéeTCommandAction est une action VCL classique La logique métier, extraite dans la commande, peut être facilement convertie en commande asynchrone, traitée dans un thread d'arrière-plan séparé. Le remplacement de la classe TCommand par TAsyncCommand est d'abord raide dans une telle transformation:
uses
Pattern.AsyncCommand;
type
TAsyncDiceRollCommand = class (TAsyncCommand)
...
end ; Bien que le changement soit très simple, mais en général, le traitement multithread est un sujet beaucoup plus grave et nécessite une connaissance plus approfondie de ce domaine. Dans cet exemple ( TDiceRollCommand ), deux sujets sont problématiques:
fProgressBar: TProgressBarfOutputRolls: TStringsVous pouvez facilement y faire face, mais cela nécessite des connaissances de traitement multithread plus générales. Plus d'informations que vous pouvez trouver dans la documentation dédiée: commande asynchrone
TCommandAction est une classe de wrapper basée sur TAction et est capable d'exécuter des commandes basées sur la classe TCommand . Le développeur, lors de la création de l'application VCL, peut facilement lier cette action à de nombreux contrôles (composants visuels qui sont motivés par des actions ou qui sont conscients de l'action). Par exemple, TCheckBox a une propriété Action qui est exécutée lorsqu'il est utilisé par l'état de cocher (cochée). Les actions présentent d'autres avantages tels que Build dans le système de notification, précisément deux de ces moteurs: un pour la mise à jour de l'état visuel et un autre, plus interne, pour avoir notifié la création de nouveaux et la suppression des composants existants. Les deux moteurs sont trop complexes pour être décrits dans cette section, plus d'informations peuvent être trouvées dans la documentation en ligne de Delphi.
La recherche de perspective architecturale de forme TCommandAction peut être utilisée comme objet invocateur et après la migration peut être remplacée par une solution personnalisée plus élastique.
Échantillon de construction sur TCommandAction Invocer:
Button1.Action := TCommandAction.Create(Button1)
.WithCaption( ' Run sample command ' )
.WithCommand(TSampleCommand.Create(Button1))
.WithInjections([Memo1, Edit1]);| Méthode utilitaire | Description |
|---|---|
WithCaption(aCaption) | Définit une légende d'action qui est affichée dans un contrôle |
WithShortCut(aShortcut) | Définit un raccourci qui active une action |
WithCommand(aCommand) | Définit une commande pour exécuter |
WithInjections(aInjections) | Injecte des valeurs dans les propriétés de la commande |
WithEventOnUpdate(aProc) | Événement déclenché après l'action Onupdate Event |
WithEventAfterExecution(aProc) | Événement déclenché lorsque la commande sera terminée |
Événement de configuration de la configuration Onupdate dans TCommandAction :
Button2.Action := TCommandAction.Create(Self)
.WithCaption( ' Run sample command ' )
.WithCommand(MySampleCommand)
.WithEventOnUpdate(
procedure(cmd: TCommandAction)
begin
cmd.Enabled := CheckBox1.Checked;
end );Le modèle TCommand permet aux développeurs d'extraire le précieux code commercial et de rendre les applications moins couplées. Les développeurs peuvent toujours utiliser des pratiques de composants bien connues et composer du code plus complexe à l'aide de composants de commande. Les développeurs peuvent même étendre le modèle de commande avec leurs propres propriétés et événements. Cependant, cette approche est une solution temporaire et doit être évoluée en une conception plus orientée objet.
Le motif TCommand est compatible avec le motif de commande GoF (voir les diagrammes ci-dessus) et peut être modernisé. Cette modération doit être démarrée lorsque la phase de refactorisation sera terminée et que la logique sera couverte par des tests unitaires. Pendant la refactorisation, toutes les dépendances visuelles doivent être supprimées, toutes les dépendances non pertinentes et le code doivent se décomposer en méthodes ou classes plus logiques plus petites.
Après la modernisation, toutes les dépendances doivent être injectées via le constructeur, la commande doit être accessible via l'interface, l'accès à la commande Les éléments internes devraient se passer via Getter et Setter Methods. Les objets composés doivent être créés à l'aide de DI Container, comme la méthode Spring4D GlobalContainer .
Exécution de la commande ad-hoc (créer, injecter, exécuter, supprimer)
TCommand.AdhocExecute<TSampleCommand>([Memo1, Edit1]);Crée des dépendances de commande et d'injecte:
cmdSampleCommand := TSampleCommand.Create(AOwner);
cmdSampleCommand.Inject([Memo1, Edit1]); Exemple de composant 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 ;