@@ -13,6 +13,10 @@ It's heavily used by cli implementations and GUI development.
1313
1414## ICommand
1515
16+ A simple command implementation is a ` ICommand ` interface + an object can be applied on by commands.
17+
18+ A Command should be a immutable object which we will represent it as a record.
19+
1620``` cs
1721var editor = new Editor (content : " hello" );
1822// wrap command info inside a instance
@@ -158,3 +162,86 @@ public class Editor
158162 }
159163}
160164```
165+
166+ ## Composite Command
167+
168+ A composite command is the combination of Command pattern and Composite pattern.
169+
170+ - Collection like to store commands with order.
171+ - Execute as chaining, be aware to handle exceptions.
172+ - Undo as chaining, be aware to handle exceptions.
173+
174+ > [ !tip]
175+ > Commands might also need context to perform actions one by one.
176+
177+ A base class can be like the following.
178+
179+ - A composite command should be ` ICommand ` too.
180+ - A composite command should be a collection like command.
181+ - Default ` Execute ` can revoke all executed when any command failed.
182+
183+ ``` cs
184+ using System .Runtime .InteropServices ;
185+
186+ public interface ICommand
187+ {
188+ void Execute ();
189+ void Undo ();
190+ bool Success { get ; set ; }
191+ }
192+
193+ public abstract class CompositeCommand <T > : List <T >, ICommand where T : class ?, ICommand
194+ {
195+ public bool Success
196+ {
197+ get => this .All (cmd => cmd .Success );
198+ set => field = value ;
199+ }
200+
201+ public virtual void Execute ()
202+ {
203+ foreach (var cmd in this )
204+ {
205+ cmd .Execute ();
206+ // if any cmd failed, revoke all executed // [!code highlight]
207+ if (! cmd .Success ) // [!code highlight]
208+ { // [!code highlight]
209+ var reverse = CollectionsMarshal .AsSpan (this )[.. IndexOf (cmd )]; // [!code highlight]
210+ reverse .Reverse (); // [!code highlight]
211+ foreach (var c in reverse ) // [!code highlight]
212+ c .Undo (); // [!code highlight]
213+ // [!code highlight]
214+ return ; // [!code highlight]
215+ } // [!code highlight]
216+ }
217+
218+ Success = true ;
219+ }
220+
221+ public virtual void Undo ()
222+ {
223+ foreach (var cmd in Enumerable .Reverse (this ))
224+ // only undo executed
225+ if (cmd .Success ) cmd .Undo ();
226+ }
227+ }
228+ ```
229+
230+ ``` cs
231+ var editor = new Editor (content : " hello" );
232+
233+ var commandInsert = new EditorCommand (editor , EditorCommandType .Insert , " , world" );
234+ var commandDelete = new EditorCommand (editor , EditorCommandType .Delete , 1 );
235+ // deletion exceeds the length. will revoke all executions. // [!code highlight]
236+ var wrongCommand = new EditorCommand (editor , EditorCommandType .Delete , 100 ); // [!code highlight]
237+
238+ // should edit back to `hello` // [!code highlight]
239+ var combined = new EditorCompositeCommand () { commandInsert , commandDelete , wrongCommand }; // [!code highlight]
240+
241+ combined .Execute ();
242+
243+ class EditorCompositeCommand : CompositeCommand <EditorCommand >
244+ {
245+
246+ }
247+ ```
0 commit comments