Когда использовать?
- Когда необходимо ответить действием на действие, callback
- Когда нужно вести очередь действий с возможностью Undo / Redo
- Когда нужно последовательное логгирование
Про этот паттерн говорят следующее.
Client – посетитель кафе, Invoker – официантка, Command – листочек с заказом блюда, Receiver – повар
Реализуем данный пример
…
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 |
program Command; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, uCommand in 'uCommand.pas'; var waiter: TWaiter; client: TClient; cook: TCook; cmd: TWaiterCommand; begin try { TODO -oUser -cConsole Main : Insert code here } waiter := TWaiter.Create(); client := TClient.Create(waiter); cook := TCook.Create(); cmd := TWaiterCommand.Create(cook); try client.Waiter.SetCommand(cmd); client.Waiter.BringMeSomeFood(); client.Waiter.TakeAwayThisPieceOfShit(); finally waiter.Free(); client.Free(); cook.Free(); cmd.Free(); end; ReadLn; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
…
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 |
unit uCommand; interface uses System.SysUtils; type // Команда TCommand = class public procedure Execute(); virtual; abstract; procedure UnExecute(); virtual; abstract; end; // Повар Receiver TCook = class public procedure CreateSomeFood(); procedure GetBackDislikedFood(); end; // Команда официантки, ConcreteComand TWaiterCommand = class(TCommand) private FCook: TCook; public constructor Create(aCook: TCook); procedure Execute(); override; procedure Unexecute(); override; end; // Официантка TWaiter = class FCommand: TCommand; public procedure SetCommand(aCmd: TCommand); procedure BringMeSomeFood(); procedure TakeAwayThisPieceOfShit(); end; TClient = class (TObject) private FWaiter: TWaiter; public constructor Create(aWaiter: TWaiter); property Waiter: TWaiter read FWaiter; end; implementation { TCook } procedure TCook.CreateSomeFood; begin Writeln('Cooking...'); end; procedure TCook.GetBackDislikedFood; begin Writeln('Hm... Someone disliked my food'); end; { TWaiterCommand } constructor TWaiterCommand.Create(aCook: TCook); begin if aCook <> nil then FCook := aCook; end; procedure TWaiterCommand.Execute; begin FCook.CreateSomeFood(); end; procedure TWaiterCommand.Unexecute; begin FCook.GetBackDislikedFood(); end; { TWaiter } procedure TWaiter.BringMeSomeFood; begin FCommand.Execute(); end; procedure TWaiter.TakeAwayThisPieceOfShit; begin FCommand.UnExecute(); end; procedure TWaiter.SetCommand(aCmd: TCommand); begin if aCmd <> nil then FCommand := aCmd; end; { TClient } constructor TClient.Create(aWaiter: TWaiter); begin if aWaiter <> nil then FWaiter := aWaiter; end; end. |
Использование Undo и Redo для данного примера. Заменим FCommand на FCommands: TList<TCommand>, немного перепишем, и получим следующее
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 |
unit uCommand; interface uses System.SysUtils, System.Generics.Collections; type // Команда TCommand = class public procedure Execute(); virtual; abstract; procedure UnExecute(); virtual; abstract; end; // Повар Receiver TCook = class public procedure CreateSomeFood(); procedure GetBackDislikedFood(); end; // Команда официантки, ConcreteComand TWaiterCommand = class(TCommand) private FCook: TCook; public constructor Create(aCook: TCook); procedure Execute(); override; procedure Unexecute(); override; end; // Официантка TWaiter = class FCommands: TList<TCommand>; FCurrentCommandIndex: integer; public constructor Create(); destructor Destroy; override; procedure AddCommand(aCmd: TCommand); procedure BringMeSomeFood(aCmdIndex: integer = -1); procedure TakeAwayThisPieceOfShit(aCmdIndex: integer = -1); procedure Undo(aDeepLevel: integer); procedure Redo(aDeepLevel: integer); property CurrentCommandIndex: integer read FCurrentCommandIndex; end; TClient = class(TObject) private FWaiter: TWaiter; public constructor Create(aWaiter: TWaiter); property Waiter: TWaiter read FWaiter; end; implementation { TCook } procedure TCook.CreateSomeFood; begin Writeln('Cooking...'); end; procedure TCook.GetBackDislikedFood; begin Writeln('Hm... Someone disliked my food'); end; { TWaiterCommand } constructor TWaiterCommand.Create(aCook: TCook); begin if aCook <> nil then FCook := aCook; end; procedure TWaiterCommand.Execute; begin FCook.CreateSomeFood(); end; procedure TWaiterCommand.Unexecute; begin FCook.GetBackDislikedFood(); end; { TWaiter } procedure TWaiter.BringMeSomeFood(aCmdIndex: integer); begin if aCmdIndex = -1 then aCmdIndex := FCommands.Count - 1; FCommands[aCmdIndex].Execute(); end; procedure TWaiter.TakeAwayThisPieceOfShit(aCmdIndex: integer); begin if aCmdIndex = -1 then aCmdIndex := FCommands.Count - 1; FCommands[aCmdIndex].UnExecute(); end; procedure TWaiter.Undo(aDeepLevel: integer); var i, index: Integer; begin index := FCurrentCommandIndex; for i := 0 to aDeepLevel - 1 do begin if index > 0 then begin FCommands[index].UnExecute(); Dec(index); end else break; end; end; constructor TWaiter.Create; begin FCommands := TList<TCommand>.Create(); end; destructor TWaiter.Destroy; begin FCommands.Free(); inherited; end; procedure TWaiter.Redo(aDeepLevel: integer); var i, index: Integer; begin index := FCurrentCommandIndex; for i := 0 to aDeepLevel - 1 do begin if index < FCommands.Count - 1 then begin FCommands[index].Execute(); Inc(index); end else break; end; end; procedure TWaiter.AddCommand(aCmd: TCommand); begin FCommands.Add(aCmd); FCurrentCommandIndex := FCommands.Count - 1; end; { TClient } constructor TClient.Create(aWaiter: TWaiter); begin if aWaiter <> nil then FWaiter := aWaiter; end; end. |