次の方法で共有


Command Design Pattern in C# 2.0

I have posted before about how to implement the observer pattern using C# in a way that is more suitable to C# and uses its functionalities to produce more elegant code.
This time I am re-implementing the Command pattern in a C# way.
First of all, let's see the classic Command Pattern UML diagram

As you can see the pattern consists of five actors

  1. Invoker
  2. Command (interface)
  3. Concrete Command class(es)
  4. Receiver
  5. Client

for more information about Command pattern check here
Now let's see how it can look like in C# world.

Let's see how the Command interface will look like

 delegate void CommandHandle<T>(T receiver);

Awesome doesn't it, just one line :)

Let's see the Command Concrete Class

 sealed class Command<T>
{
    T _receiver;

    CommandHandle<T> _doCommand;
    CommandHandle<T> _undoCommand;

    public Command(CommandHandle<T> doCommand, T receiver)
    {
        _doCommand = doCommand;
        _receiver = receiver;
    }

    public Command(CommandHandle<T> doCommand, CommandHandle<T> undoCommand, T receiver)
        : this(doCommand, receiver)
    {
        _undoCommand = undoCommand;
    }

    public void Execute()
    {
        if(_doCommand != null)
            _doCommand(_receiver);
    }

    public void Undo()
    {
        if(_undoCommand != null)
            _undoCommand(_receiver);
    }
}

This is a generic class so it can be used for most cases instead of creating multiple inherited classes, the class has two references of the CommandHandler delegate one for the DoCommand and the other is for the UndoCommand

Let's see how the Receiver class will look like,

 class Receiver
{
    private int _state;

    public int State
    {
        get { return _state; }
        set { _state = value; }
    }
}

Actually it doesn't matter how it looks like, so the previous code is just a sample class.

Let's now see how the Invoker class will look like

 class Invoker
{
    List<Command<Receiver>> _commands;

    public Invoker()
    {
        _commands = new List<Command<Receiver>>();
    }

    public void AddCommand(Command<Receiver> command)
    {
        _commands.Add(command);
    }

    public void RemoveCommand(Command<Receiver> command)
    {
        _commands.Remove(command);
    }

    public void CallCommands()
    {
        foreach (Command<Receiver> command in _commands)
        {
            command.Execute();
        }
    }

    public void UndoCommands()
    {
        foreach (Command<Receiver> command in _commands)
        {
            command.Undo();
        }
    }
}

The invoker is just a class that is interested in the Receiver objects and want to apply certain actions on instance(s) of the Receiver class, so it uses a list of Command<Receiver> to encapsulate the executing code.

Finally, we should see how the Client will use these classes.

 class Program
{
    static void Main(string[] args)
    {
        Receiver recv1 = new Receiver();
        Receiver recv2 = new Receiver();
        Command<Receiver> command1 = new Command<Receiver>(new CommandHandle<Receiver>(Func1), new CommandHandle<Receiver>(UndoFunc1), recv1);
        Command<Receiver> command2 = new Command<Receiver>(new CommandHandle<Receiver>(Func2), recv2);
        Invoker invoker = new Invoker();
        invoker.AddCommand(command1);
        invoker.AddCommand(command2);
        invoker.CallCommands();
        invoker.UndoCommands();

    }

    static void Func1(Receiver recv1)
    {
        recv1.State ++;
        Console.WriteLine(recv1.State);
    }

    static void Func2(Receiver recv2)
    {
        recv2.State+=2;
    }

    static void UndoFunc1(Receiver recv)
    {
        recv.State--;
        Console.WriteLine(recv.State);
    }
}

So the main difference between the classic implementation and this one, that

  1. We didn't define an interface and we juts used a generic delegate
  2. We don't need to inherit from the interface every time we want to use the command pattern for different Receiver type, we replaced this with a generic Command class.

I hope you like it, I attached the previous code with the post
Have fun
kick it on DotNetKicks.com