Skip to content

Commit d06ec7b

Browse files
committed
Add Command pattern implementation and tests
Implemented the Command design pattern with a `Light` receiver, `LightOnCommand` and `LightOffCommand` classes, and a `RemoteControl` invoker. Updated `Program.cs` to include a demo showcasing command execution and undo functionality. Introduced the `ICommand` interface to define the contract for commands. Updated `README.md` to document the Command pattern, its purpose, usage, and related files. Added `CommandTests.cs` to verify the functionality of the Command pattern, including execution and undo scenarios.
1 parent 97a15ee commit d06ec7b

File tree

8 files changed

+141
-1
lines changed

8 files changed

+141
-1
lines changed

DesignPatterns/Command/ICommand.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace DesignPatterns.Command;
2+
3+
public interface ICommand
4+
{
5+
void Execute();
6+
void Undo();
7+
}

DesignPatterns/Command/Light.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace DesignPatterns.Command;
2+
3+
public class Light
4+
{
5+
public bool IsOn { get; private set; }
6+
7+
public void On() => IsOn = true;
8+
public void Off() => IsOn = false;
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace DesignPatterns.Command;
2+
3+
public class LightOffCommand : ICommand
4+
{
5+
private readonly Light _light;
6+
7+
public LightOffCommand(Light light) => _light = light;
8+
9+
public void Execute() => _light.Off();
10+
public void Undo() => _light.On();
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace DesignPatterns.Command;
2+
3+
public class LightOnCommand : ICommand
4+
{
5+
private readonly Light _light;
6+
7+
public LightOnCommand(Light light) => _light = light;
8+
9+
public void Execute() => _light.On();
10+
public void Undo() => _light.Off();
11+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace DesignPatterns.Command;
2+
3+
public class RemoteControl
4+
{
5+
private ICommand? _slot;
6+
private readonly Stack<ICommand> _history = new();
7+
8+
public void SetCommand(ICommand command) => _slot = command;
9+
10+
public void PressButton()
11+
{
12+
_slot?.Execute();
13+
if (_slot != null) _history.Push(_slot);
14+
}
15+
16+
public void PressUndo()
17+
{
18+
if (_history.Any())
19+
{
20+
var cmd = _history.Pop();
21+
cmd.Undo();
22+
}
23+
}
24+
}

DesignPatterns/Program.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
ITextProvider provider = new LegacyTextAdapter(legacy);
6767
Console.WriteLine(provider.GetText());
6868

69+
6970
// Bridge demo
7071
Console.WriteLine();
7172
Console.WriteLine("Bridge pattern demo:");
@@ -110,6 +111,28 @@
110111
Console.WriteLine(h1.Handle(new Request(2, "r2")));
111112
Console.WriteLine(h1.Handle(new Request(99, "r3")));
112113

114+
// Command demo
115+
Console.WriteLine();
116+
Console.WriteLine("Command pattern demo:");
117+
var light = new DesignPatterns.Command.Light();
118+
var lightOn = new DesignPatterns.Command.LightOnCommand(light);
119+
var lightOff = new DesignPatterns.Command.LightOffCommand(light);
120+
var cmdRemote = new DesignPatterns.Command.RemoteControl();
121+
122+
cmdRemote.SetCommand(lightOn);
123+
cmdRemote.PressButton();
124+
Console.WriteLine($"Light is on: {light.IsOn}");
125+
126+
cmdRemote.SetCommand(lightOff);
127+
cmdRemote.PressButton();
128+
Console.WriteLine($"Light is on: {light.IsOn}");
129+
130+
// demonstrate undo
131+
cmdRemote.SetCommand(lightOn);
132+
cmdRemote.PressButton();
133+
cmdRemote.PressUndo();
134+
Console.WriteLine($"After undo, light is on: {light.IsOn}");
135+
113136
// Decorator demo
114137
Console.WriteLine();
115138
Console.WriteLine("Decorator pattern demo:");

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Design Patterns Examples
33
This solution demonstrates simple design pattern examples in C# targeting .NET 10 (C# 14).
44

55
Projects
6-
- `DesignPatterns` - Console demo showing Factory, Singleton, Builder, Prototype, Abstract Factory, Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy, and Chain of Responsibility patterns.
6+
- `DesignPatterns` - Console demo showing Factory, Singleton, Builder, Prototype, Abstract Factory, Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy, Chain of Responsibility, and Command patterns.
77
- Factory implementation: `DesignPatterns/Factories`
88
- Singleton implementation: `DesignPatterns/Singleton/Logger.cs`
99
- Builder implementation: `DesignPatterns/Builder/HouseBuilder.cs` and `DesignPatterns/Builder/House.cs`
@@ -17,6 +17,7 @@ Projects
1717
- Flyweight implementation: `DesignPatterns/Flyweight/*`
1818
- Proxy implementation: `DesignPatterns/Proxy/*`
1919
- Chain of Responsibility implementation: `DesignPatterns/Chain/*`
20+
- Command implementation: `DesignPatterns/Command/*`
2021
- `Tests` - xUnit tests covering the examples.
2122

2223
Included patterns and brief docs
@@ -173,6 +174,20 @@ Included patterns and brief docs
173174
Console.WriteLine(h1.Handle(new Request(99, "r3")));
174175
```
175176
177+
- Command Pattern
178+
- Purpose: Encapsulate a request as an object, thereby letting you parameterize clients with queues, requests, and operations and support undoable operations.
179+
- Example: `DesignPatterns/Command/*` contains `ICommand`, `Light`, `LightOnCommand`, `LightOffCommand`, and `RemoteControl` (invoker).
180+
- Usage snippet:
181+
182+
```csharp
183+
var light = new Light();
184+
var on = new LightOnCommand(light);
185+
var remote = new RemoteControl();
186+
remote.SetCommand(on);
187+
remote.PressButton();
188+
remote.PressUndo();
189+
```
190+
176191
Tests
177192
- Tests are written with xUnit in the `Tests` project.
178193
- Factory tests: `Tests/ShapeFactoryTests.cs`
@@ -188,6 +203,7 @@ Tests
188203
- Flyweight tests: `Tests/FlyweightTests.cs`
189204
- Proxy tests: `Tests/ProxyTests.cs`
190205
- Chain tests: `Tests/ChainTests.cs`
206+
- Command tests: `Tests/CommandTests.cs`
191207
192208
Common commands
193209
- Build solution: `dotnet build`

Tests/CommandTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using DesignPatterns.Command;
2+
3+
namespace Tests;
4+
5+
public class CommandTests
6+
{
7+
[Fact]
8+
public void LightOnCommand_ExecutesAndUndo()
9+
{
10+
var light = new Light();
11+
var on = new LightOnCommand(light);
12+
13+
var remote = new RemoteControl();
14+
remote.SetCommand(on);
15+
16+
remote.PressButton();
17+
Assert.True(light.IsOn);
18+
19+
remote.PressUndo();
20+
Assert.False(light.IsOn);
21+
}
22+
23+
[Fact]
24+
public void LightOffCommand_ExecutesAndUndo()
25+
{
26+
var light = new Light();
27+
light.On();
28+
var off = new LightOffCommand(light);
29+
30+
var remote = new RemoteControl();
31+
remote.SetCommand(off);
32+
33+
remote.PressButton();
34+
Assert.False(light.IsOn);
35+
36+
remote.PressUndo();
37+
Assert.True(light.IsOn);
38+
}
39+
}

0 commit comments

Comments
 (0)