Design patterns in C# – part 17 (Command pattern)

Role

The Command pattern creates distance between the client that requests an operation and the object that can perform it.

Design

CommandPattern

Implementation

  • Client -> TestCommandPattern
  • Invoker -> delegate Invoker
  • ICommand -> AbstractCommand
  • Command -> Paste, Print
using System;

namespace CommandPattern
{
    public class TestCommandPattern
    {
        public static void Main(string[] args)
        {
            var document = new Document("jacek_document.txt");

            var paste = new CommandMenu.Paste(document);
            var print = new CommandMenu.Print(document);

            CommandMenu.Clipboard = "Very important text";
            paste.Execute();
            print.Execute();
            paste.Undo();

            CommandMenu.Clipboard = "Not necessary text";
            paste.Execute();
            CommandMenu.Clipboard = "Hello world";
            paste.Redo();
            print.Execute();
            print.Undo();

            Console.WriteLine("Logged {0} commands", paste.Execute.Count());
        }
    }

    public delegate void Invoker();

    public static class InvokerExtensions
    {
        private static int _count;

        public static int Count(this Invoker invoker)
        {
            return _count;
        }

        public static void Log(this Invoker invoker)
        {
            _count++;
        }
    }

    public class CommandMenu
    {
        public static string Clipboard;

        #region Nested type: AbstractCommand

        public abstract class AbstractCommand
        {
            public Invoker Execute;
            public Invoker Redo;
            public Invoker Undo;
        }

        #endregion

        #region Nested type: Paste

        public class Paste : AbstractCommand
        {
            public Paste(Document document)
            {
                Execute = delegate
                              {
                                  Execute.Log();
                                  document.Paste();
                              };
                Redo = delegate
                           {
                               Redo.Log();
                               document.Paste();
                           };
                Undo = delegate
                           {
                               Undo.Log();
                               document.Restore();
                           };
            }
        }

        #endregion

        #region Nested type: Print

        public class Print : AbstractCommand
        {
            public Print(Document document)
            {
                Execute = delegate
                              {
                                  Execute.Log();
                                  document.Print();
                              };
                Redo = delegate
                           {
                               Redo.Log();
                               document.Print();
                           };
                Undo = delegate
                           {
                               Undo.Log();
                               Console.WriteLine("Cannot undo a Print ");
                           };
            }
        }

        #endregion
    }

    public class Document
    {
        private readonly string _name;
        private string _oldpage, _page;

        public Document(string name)
        {
            this._name = name;
        }

        public void Print()
        {
            Console.WriteLine("File {0} at {1} \n {2}", this._name, DateTime.Now, this._page);
        }

        public void Restore()
        {
            this._page = this._oldpage;
        }

        public void Paste()
        {
            this._oldpage = this._page;
            this._page = String.Concat(this._page, CommandMenu.Clipboard, Environment.NewLine);
        }
    }
}

OUTPUT:

File jacek_document.txt at 2009-07-29 06:00:38
Very important text

File jacek_document.txt at 2009-07-29 06:00:38
Not necessary text
Hello world

Cannot undo a Print
Logged 7 commands

Use when

You have:

  • Commands that different receivers can handle in different ways
  • A high-level set of commands that are implemented by primitive operations

You want to:

  • Specify, queue, and execute commands at different times
  • Support an Undo function for commands
  • Support auditing and logging of all changes via commands

Bibliography

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s