Упрощенная версия командного шаблона GOF, созданная для целей модернизации проектов VCL. Также добавил фабрику действия к этому проекту, который завершает команду в действие VCL.

Проект содержит две версии реализации шаблона:
ICommandTCommand Class на основе TCOMPONTONT Компонент TCommand был создан, чтобы помочь модернизации устаревшего кода VCL . Он помогает извлечению запутанного кода, который после закрепления его с помощью модульных тестов может быть переработана в более чистый и дешевый для поддержания объектно-ориентированного кода.
Компонент TCommand - это переходной объект, который следует рефактовать после распределения извлеченного кода и после удаления зависимостей пользовательского интерфейса

Самый простой способ использования компонента TCommand - это создать новый класс, вставить длинный метод в метод выполнения и добавить все зависимости в качестве опубликованных свойств. Смотрите образец реже.
Диаграмма использования TCommand в приложении VCL:

Разработчик для создания новой команды должен определить новый класс, полученный из TCommand (Unit: Pattern.Command.pas ) и реализует защищенный метод DoExecute , который содержит основную логику команды.
Разработчик также может реализовать метод DoGuard , который называется перед DoExecute и позволяет проверить все обязательные инъекции (система инъекции объясняется ниже). Обычно все инъекции проверяются с вызовом Assert.
Образец команды без инъекции (пустая охрана):
type
TDiceRollCommand = class (TCommand)
protected
procedure DoExecute ; override;
end ;
procedure TDiceRollCommand.DoExecute ;
begin
ShowMessage( ' Dice roll: ' +RandomRange( 1 , 6 ).ToString);
end ; Чтобы выполнить команду, вы должны создать объект и Execute публичный метод, который вызывает DoGuard , а затем DoExecute :
cmd := TDiceRollCommand.Ceate(Self);
cmd.Execute; Компонент TCommand имеет встроенную систему автоматической инъекции, основанную на классическом механизме RTTI , используемом дизайнером IDE (инспектор объектов). Свойства, подвергшиеся воздействию инъекции, должны быть определены в 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]);Наиболее популярный и обычно рекомендуемый метод инъекции зависимостей является инъекцией конструктора. Это решение, представленное здесь (Tommmand Pattern), является более на основе компонентов подхода. Этот шаблон больше похож на этап перехода, который позволяет быстро извлекать и выполнять важные части большого приложения. Окончательная целевая точка в этом процессе является лучшим архитектурным решением, означает инъекцию через конструктор и используйте интерфейсы вместо объектов.
TCommand.AdhocExecute<T> - выполняет команду (создает команду, инъекции зависимости выполняют ее и удаляет)InjectExecuteTCommandAction , которая выполняет команду при использовании действияTCommandAction - это классическое VCL Action Бизнес -логика, извлеченная в команду, может быть легко преобразована в асинхронную команду, обработанную в отдельном фоновом потоке. Замена класса TCommand на TAsyncCommand впервые за крути в такой трансформации:
uses
Pattern.AsyncCommand;
type
TAsyncDiceRollCommand = class (TAsyncCommand)
...
end ; Хотя изменения очень просты, но в целом многопоточная обработка является гораздо более серьезным предметом и требует более глубоких знаний об этой области. В этом примере ( TDiceRollCommand ) две темы проблематичны:
fProgressBar: TProgressBarfOutputRolls: TStringsВы можете легко справиться с ними, но это требует более общих знаний о многопоточной обработке. Более подробную информацию вы можете найти в специальной документации: асинхронная команда
TCommandAction - это класс обертки, основанный на TAction и способен выполнять команды на основе класса TCommand . Разработчик при создании приложения VCL может легко связать это действие со многими элементами управления (визуальные компоненты, которые обусловлены действиями или являются с учетом действий). Например, у TCheckBox есть свойство Action , которое выполняется при использовании, изменяет состояние флажки (проверено). Действия имеют некоторые другие преимущества, такие как сборка в системе уведомлений, именно два таких двигателя: один для обновления визуального состояния и другого, более внутреннего, для уведомления о создании новых и удалений существующих компонентов. Оба двигателя слишком сложны, чтобы их можно было описать в этом разделе, в онлайн -документации Delphi можно найти больше информации.
В качестве объекта Invoker может использоваться архитектурная 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) | Событие запускается, когда команда будет закончена |
Образец настройки onupdate событие в TCommandAction :
Button2.Action := TCommandAction.Create(Self)
.WithCaption( ' Run sample command ' )
.WithCommand(MySampleCommand)
.WithEventOnUpdate(
procedure(cmd: TCommandAction)
begin
cmd.Enabled := CheckBox1.Checked;
end );Паттерн Tommmand позволяет разработчикам извлекать ценный бизнес -код и сделать приложения менее связанными. Одновременно разработчики все еще могут использовать хорошо известную практику компонентов и составлять более сложный код, используя компоненты команд. Разработчики могут даже расширять шаблон команд со своими собственными свойствами и событиями. Однако этот подход является временным решением и должен быть превращен в более ориентированный на объект дизайн.
Паттерн Tommmand совместим с шаблоном команды GOF (см. Диаграммы выше) и может быть модернизирован. Эта модерация должна быть запущена, когда фаза рефакторирования будет завершена, и логика будет покрыта модульными тестами. Во время рефакторинга все визуальные зависимости должны быть удалены, а также все нерелевантные зависимости, и код должен разбиваться на более мелкие более логические методы или классы.
После модернизации все зависимости должны быть введены через конструктор, команду следует получить через интерфейс, доступ к командам к внутренним элементам доступа к методам Getter и Setter. Составленные объекты должны создаваться с использованием контейнера DI, например, Spring4D GlobalContainer Method.
Ad-Hoc выполнение команды (создать, вводить, выполнить, удалить)
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 ;