-
Notifications
You must be signed in to change notification settings - Fork 6
/
Vcl.Pattern.Command.pas
169 lines (150 loc) · 4.65 KB
/
Vcl.Pattern.Command.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
{ * ------------------------------------------------------------------------
* ♥
* ♥ VCL Command component/class with a factory
* ♥
* Components: TCommand, TCommandAction
* Classes: TCommandVclFactory
* Project: https://github.com/bogdanpolak/command-delphi
* Documentation: on the github site
* ReleaseDate: ↓ see Signature below ↓
* ReleaseVersion: ↓ see Signature below ↓
* ------------------------------------------------------------------------ }
unit Vcl.Pattern.Command;
interface
uses
System.Classes, System.SysUtils, System.Actions, System.TypInfo,
Vcl.ActnList;
type
ICommand = interface
procedure Execute();
end;
TCommand = class(TComponent, ICommand)
const
// * --------------------------------------------------------------------
// * Signature
ReleaseDate = '2019.08.28';
ReleaseVersion = '0.2';
// * --------------------------------------------------------------------
strict private
// FReceiver: TReceiver;
strict protected
// procedure Guard; - assert injections of all required properties
procedure Guard; virtual; abstract;
public
procedure Execute; virtual;
// call receiver method(s) or just do the job (merged command)
// property Receiver: TReceiver read FReceiver set FReceiver;
end;
TCommandVclFactory = class(TComponent)
private
class procedure InjectProperties(ACommand: TCommand;
const Injections: array of const);
public
class function CreateCommand<T: TCommand>(AOwner: TComponent;
const Injections: array of const): T;
class procedure ExecuteCommand<T: TCommand>(const Injections
: array of const);
class function CreateCommandAction<T: TCommand>(AOwner: TComponent;
const ACaption: string; const Injections: array of const): TAction;
end;
TCommandAction = class(TAction)
strict private
FCommand: TCommand;
procedure OnExecuteEvent(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
property Command: TCommand read FCommand write FCommand;
end;
implementation
// ------------------------------------------------------------------------
{ TCommand }
procedure TCommand.Execute;
begin
Guard;
end;
// ------------------------------------------------------------------------
{ TCommandAction }
constructor TCommandAction.Create(AOwner: TComponent);
begin
inherited;
Self.OnExecute := OnExecuteEvent;
end;
procedure TCommandAction.OnExecuteEvent(Sender: TObject);
begin
Assert(Command <> nil);
FCommand.Execute;
end;
// ------------------------------------------------------------------------
{ TActionFactory }
class procedure TCommandVclFactory.InjectProperties(ACommand: TCommand;
const Injections: array of const);
var
PropList: PPropList;
PropCount: Integer;
i: Integer;
j: Integer;
begin
// Inject dependencies to the command.
// Limitations of this version:
// * only TObject and descendants injections is supported
// * properties must have different types (ClassName)
try
PropCount := System.TypInfo.GetPropList(ACommand, PropList);
for i := 0 to PropCount - 1 do
begin
if PropList^[i].PropType^.Kind = tkClass then
begin
// Do injection
for j := 0 to High(Injections) do
if Injections[j].VType = vtObject then
begin
// PropList^[i].PropType^.Name - ClassName of the property
if Injections[j].VObject.ClassName = String
(PropList^[i].PropType^.Name) then
SetObjectProp(ACommand, String(PropList^[i].Name),
Injections[j].VObject);
end
else
Assert(False,
'Not supported yet! Only objects can be injected to a command');
end;
end;
finally
FreeMem(PropList);
end;
end;
class function TCommandVclFactory.CreateCommand<T>(AOwner: TComponent;
const Injections: array of const): T;
begin
Result := T.Create(AOwner);
InjectProperties(Result, Injections);
end;
class procedure TCommandVclFactory.ExecuteCommand<T>(const Injections
: array of const);
var
Command: T;
begin
try
Command := T.Create(nil);
InjectProperties(Command, Injections);
Command.Execute;
finally
Command.Free;
end;
end;
class function TCommandVclFactory.CreateCommandAction<T>(AOwner: TComponent;
const ACaption: string; const Injections: array of const): TAction;
var
act: TCommandAction;
begin
act := TCommandAction.Create(AOwner);
with act do
begin
Command := T.Create(act);
Caption := ACaption;
end;
InjectProperties(act.Command, Injections);
Result := act;
end;
// ------------------------------------------------------------------------
end.