Design Patterns
Example 2: Undo/Redo Functionality

using System;
using System.Collections.Generic;

// Command interface
public interface ICommand
{
    void Execute();
    void Undo();
}

// Concrete command
public class AddCommand : ICommand
{
    private Calculator _calculator;
    private int _value;

    public AddCommand(Calculator calculator, int value)
    {
        _calculator = calculator;
        _value = value;
    }

    public void Execute()
    {
        _calculator.Add(_value);
    }

    public void Undo()
    {
        _calculator.Subtract(_value);
    }
}

// Receiver
public class Calculator
{
    private int _currentValue;

    public void Add(int value)
    {
        _currentValue += value;
        Console.WriteLine($"Added {value}. Current value = {_currentValue}");
    }

    public void Subtract(int value)
    {
        _currentValue -= value;
        Console.WriteLine($"Subtracted {value}. Current value = {_currentValue}");
    }
}

// Invoker
public class Invoker
{
    private Stack _commands = new Stack();

    public void ExecuteCommand(ICommand command)
    {
        command.Execute();
        _commands.Push(command);
    }

    public void UndoLastCommand()
    {
        if (_commands.Count > 0)
        {
            ICommand lastCommand = _commands.Pop();
            lastCommand.Undo();
        }
        else
        {
            Console.WriteLine("No more commands to undo");
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Client code
        Calculator calculator = new Calculator();
        Invoker invoker = new Invoker();

        ICommand addCommand = new AddCommand(calculator, 10);
        invoker.ExecuteCommand(addCommand);

        ICommand subtractCommand = new AddCommand(calculator, 5);
        invoker.ExecuteCommand(subtractCommand);

        invoker.UndoLastCommand(); // Undo subtractCommand
        invoker.UndoLastCommand(); // Undo addCommand
    }
}

Conclusion

In essence, the Command Pattern promotes loose coupling between objects by turning requests into objects. This flexibility and separation of concerns make it a powerful tool in designing systems where operations need to be encapsulated, queued, logged, and possibly undone.