Versão simplificada do padrão de comando GOF, criado para fins de modernização dos projetos VCL. Também acrescentou a Factory de ação a este projeto, que está envolvendo um comando na ação VCL.

O projeto contém duas versões da implementação do padrão:
ICommand InterfaceTCommand Class baseada no TComponent O componente TCommand foi criado para ajudar a modernização do código VCL herdado . Auxilia a extração do código emaranhado, que depois de protegê-lo com testes de unidade, pode ser refaturado em mais limpos e mais baratos para manter o código orientado a objetos.
O componente TCommand é um objeto de transição que deve ser refaturado após a limpeza do código extraído e após a remoção de dependências da interface do usuário

A maneira mais fácil de usar o componente TCommand é criar uma nova classe, colar o método Long no método executado e adicionar todas as dependências como propriedades publicadas. Veja a amostra abaixo.
Diagrama do uso do TCommand no aplicativo VCL:

O desenvolvedor para criar um novo comando precisa definir uma nova classe derivada do TCommand (unidade: Pattern.Command.pas ) e implementa um método protegido DoExecute , que contém uma lógica de comando principal.
O desenvolvedor também pode implementar um método DoGuard , chamado antes DoExecute e permitir verificar todas as injeções obrigatórias (o sistema de injeção é explicado abaixo). Normalmente, todas as injeções são verificadas com a chamada assert.
Comando de amostra sem injeção (guarda vazia):
type
TDiceRollCommand = class (TCommand)
protected
procedure DoExecute ; override;
end ;
procedure TDiceRollCommand.DoExecute ;
begin
ShowMessage( ' Dice roll: ' +RandomRange( 1 , 6 ).ToString);
end ; Para executar o comando, você deve criar objeto e Execute o método público, que chama DoGuard e depois DoExecute :
cmd := TDiceRollCommand.Ceate(Self);
cmd.Execute; O componente TCommand construiu um sistema de injeção automatizada com base no mecanismo RTTI clássico usado pelo IDE Form Designer (Object Inspetor). As propriedades expostas a serem injetáveis devem ser definidas na seção published do componente (comando). Todas as classes baseadas em componentes ativaram a geração de informações do tipo de tempo de execução durante o processo de compilação (opção compilador {$TYPEINFO ON} ). Obrigado durante a criação de novo comando, todas as dependências podem ser facilmente fornecidas e atribuídas às propriedades publicadas automaticamente. Mais informações sobre o mecanismo RTTI clássico podem ser encontradas na Documentação Delphi: Informações do tipo de tempo de execução
Comando de amostra com duas dependências (uma necessária e uma opcional):
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 ;As propriedades publicadas disponíveis do TCommand são comparadas aos tipos de parâmetros passados em parâmetros (Array Open). As regras a seguir são usadas pela correspondência do algoritmo:
Aviso! O objeto injetado é acessado por endereço na memória (ponteiro), graças a que todas as alterações feitas no objeto são visíveis dentro e fora do tcommand. Tipos e strings simples são acessados por valor e propriedades devem ser atualizadas manualmente para serem atualizadas.
Código de amostra Injetando objetos nas propriedades do tdicerollCommand:
cmd := TDiceRollCommand.Create(Self)
.Inject([Memo1.Lines,ProgressBar1]);O método mais popular e geralmente aconselhado para injetar dependências é uma injeção de construtor. Esta solução introduzida aqui (padrão tCommand) é uma abordagem mais baseada em componentes. Esse padrão é mais como um estágio de transição que permite extrair e executar rapidamente partes importantes de grande aplicação. O ponto de destino final nesse processo é a melhor solução arquitetônica, significa injeção através do construtor e usar interfaces em vez de objetos.
TCommand.AdhocExecute<T> - Executa um comando (cria um comando, injeta dependências o executa e remove)InjectExecuteTCommandAction , que executa o comando quando a ação é invocadaTCommandAction é uma ação clássica de VCL A lógica de negócios, extraída para o comando, pode ser facilmente convertida em comando assíncrono, processado em um encadeamento de segundo plano separado. Substituindo a aula TCommand pelo TAsyncCommand é primeiro íngreme em tal transformação:
uses
Pattern.AsyncCommand;
type
TAsyncDiceRollCommand = class (TAsyncCommand)
...
end ; Embora a mudança seja muito simples, mas, em geral, o processamento multithread é um assunto muito mais sério e requer conhecimento mais profundo dessa área. Neste exemplo ( TDiceRollCommand ), dois tópicos são problemáticos:
fProgressBar: TProgressBarfOutputRolls: TStringsVocê pode lidar facilmente com eles, mas isso requer conhecimento de processamento multithread mais geral. Mais informações que você pode encontrar na documentação dedicada: comando assíncrono
TCommandAction é uma classe de invólucro baseada na TAction e é capaz de executar comandos com base na classe TCommand . O desenvolvedor, ao criar um aplicativo VCL, pode facilmente vincular essa ação a muitos controles (componentes visuais que são conduzidos por ações ou com reconhecimento de ação). Por exemplo, TCheckBox possui propriedade Action que é executada quando usado é alterar o estado da caixa de seleção (verificado). As ações têm outras vantagens, como o Build in Notification System, precisamente dois desses motores: um para atualizar o estado visual e outro, mais interno, para notificar sobre a criação de novos e exclusão de componentes existentes. Ambos os motores são complexos demais para serem descritos nesta seção, mais informações podem ser encontradas na documentação online Delphi.
A perspectiva arquitetônica da forma de aparência TCommandAction pode ser usada como um objeto de invasor e após a migração pode ser substituído por uma solução personalizada mais elástica.
Amostra de construção no TCommandAction Invocador:
Button1.Action := TCommandAction.Create(Button1)
.WithCaption( ' Run sample command ' )
.WithCommand(TSampleCommand.Create(Button1))
.WithInjections([Memo1, Edit1]);| Método de utilidade | Descrição |
|---|---|
WithCaption(aCaption) | Define uma legenda de ação que é exibida em um controle |
WithShortCut(aShortcut) | Define um atalho que está ativando uma ação |
WithCommand(aCommand) | Define um comando para executar |
WithInjections(aInjections) | Injeta valores nas propriedades do comando |
WithEventOnUpdate(aProc) | Evento acionado após a ação OnUpdate Event |
WithEventAfterExecution(aProc) | Evento acionado quando o comando for concluído |
Evento de configuração de amostra OnUpdate no TCommandAction :
Button2.Action := TCommandAction.Create(Self)
.WithCaption( ' Run sample command ' )
.WithCommand(MySampleCommand)
.WithEventOnUpdate(
procedure(cmd: TCommandAction)
begin
cmd.Enabled := CheckBox1.Checked;
end );O padrão TCommand permite que os desenvolvedores extraem o valioso código comercial e tornem os aplicativos menos acoplados. Os desenvolvedores simultaneamente ainda podem usar práticas de componentes bem conhecidas e compor código mais complexo usando componentes de comando. Os desenvolvedores podem até expandir o padrão de comando com suas próprias propriedades e eventos. No entanto, essa abordagem é uma solução temporária e deve ser evoluída para um design mais orientado a objetos.
O padrão tCommand é compatível com o padrão de comando do GOF (consulte os diagramas acima) e pode ser modernizado. Essa moderação deve ser iniciada quando a fase de refatoração será concluída e a lógica será coberta por testes de unidade. Durante a refatoração, todas as dependências visuais devem ser removidas, também todas as dependências irrelevantes e o código deve estar dividido em métodos ou classes menores mais lógicos.
Após a modernização, todas as dependências devem ser injetadas através do construtor, o comando deve ser acessado através da interface, o acesso aos itens internos do comando deve ser através dos métodos Getter e Setter. Objetos compostos devem ser criados usando o contêiner DI, como o método Spring4D GlobalContainer .
Execução do comando ad-hoc (Criar, injetar, executar, remover)
TCommand.AdhocExecute<TSampleCommand>([Memo1, Edit1]);Cria comando e injete dependências:
cmdSampleCommand := TSampleCommand.Create(AOwner);
cmdSampleCommand.Inject([Memo1, Edit1]); Exemplo de componente 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 ;