Skip to content

Commit

Permalink
grrr
Browse files Browse the repository at this point in the history
  • Loading branch information
sharpchen committed Nov 2, 2024
1 parent ee80471 commit 6c2f82c
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 75 deletions.
87 changes: 87 additions & 0 deletions docs/document/Csharp Design Patterns/docs/Behavioural/Command.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ It's heavily used by cli implementations and GUI development.

## ICommand

A simple command implementation is a `ICommand` interface + an object can be applied on by commands.

A Command should be a immutable object which we will represent it as a record.

```cs
var editor = new Editor(content: "hello");
// wrap command info inside a instance
Expand Down Expand Up @@ -158,3 +162,86 @@ public class Editor
}
}
```

## Composite Command

A composite command is the combination of Command pattern and Composite pattern.

- Collection like to store commands with order.
- Execute as chaining, be aware to handle exceptions.
- Undo as chaining, be aware to handle exceptions.

> [!tip]
> Commands might also need context to perform actions one by one.
A base class can be like the following.

- A composite command should be `ICommand` too.
- A composite command should be a collection like command.
- Default `Execute` can revoke all executed when any command failed.

```cs
using System.Runtime.InteropServices;

public interface ICommand
{
void Execute();
void Undo();
bool Success { get; set; }
}

public abstract class CompositeCommand<T> : List<T>, ICommand where T : class?, ICommand
{
public bool Success
{
get => this.All(cmd => cmd.Success);
set => field = value;
}

public virtual void Execute()
{
foreach (var cmd in this)
{
cmd.Execute();
// if any cmd failed, revoke all executed // [!code highlight]
if (!cmd.Success) // [!code highlight]
{ // [!code highlight]
var reverse = CollectionsMarshal.AsSpan(this)[..IndexOf(cmd)]; // [!code highlight]
reverse.Reverse(); // [!code highlight]
foreach (var c in reverse) // [!code highlight]
c.Undo(); // [!code highlight]
// [!code highlight]
return; // [!code highlight]
} // [!code highlight]
}

Success = true;
}

public virtual void Undo()
{
foreach (var cmd in Enumerable.Reverse(this))
// only undo executed
if (cmd.Success) cmd.Undo();
}
}
```

```cs
var editor = new Editor(content: "hello");

var commandInsert = new EditorCommand(editor, EditorCommandType.Insert, ", world");
var commandDelete = new EditorCommand(editor, EditorCommandType.Delete, 1);
// deletion exceeds the length. will revoke all executions. // [!code highlight]
var wrongCommand = new EditorCommand(editor, EditorCommandType.Delete, 100); // [!code highlight]
// should edit back to `hello` // [!code highlight]
var combined = new EditorCompositeCommand() { commandInsert, commandDelete, wrongCommand }; // [!code highlight]
combined.Execute();

class EditorCompositeCommand : CompositeCommand<EditorCommand>
{

}
```
Loading

0 comments on commit 6c2f82c

Please sign in to comment.