C# design pattern-4 structural pattern-4.3 Command Pattern

Posted by BANDYCANDY on Sun, 06 Mar 2022 05:55:21 +0100

4.3.1 definition

Command mode encapsulates a request as an object, so that you can parameterize customers with different requests, queue or log requests, and support revocable operations.

The command mode belongs to the behavior mode of the object. Command mode abstracts an operation or behavior into an object, and separates the responsibility of issuing the command from the responsibility of executing the command through the abstraction of the command. The implementation of command mode can provide the function of revocation and recovery of commands.

4.3.2 structure diagram

As can be seen from the structure diagram of the command mode, it involves five roles, which are:

Customer role: issue a specific command and determine its recipient.

Command role: declares an abstract interface implemented for all concrete command classes

Specific command role: it defines a weak coupling between the receiver and the behavior, and is responsible for calling the corresponding methods of the receiver.

Requester role: responsible for calling the command object to execute the command.

Receiver role: responsible for the execution of specific actions.

4.3.3 general code

  /// <summary>
    /// Command Class, which is used to declare the interface to perform operations
    /// </summary>
    public abstract class Command
    {
        protected Receiver _receiver;
        public Command(Receiver receiver)
        {
            this._receiver = receiver;
        }
        public abstract void Execute();
    }
    /// <summary>
    /// ConcreteCommand Class, bind a receiver object to an action, and call the corresponding action of the receiver to realize Execute. 
    /// </summary>
    public class ConcreteCommand : Command
    {
        public ConcreteCommand(Receiver receiver) : base(receiver)
        {
        }

        public override void Execute()
        {
            _receiver.Action();
        }
    }
    /// <summary>
    /// Receiver Class that knows how to implement and perform an operation related to a request. Any class may act as a recipient
    /// </summary>
    public class Receiver
    {
        public void Action()
        {
            Console.WriteLine("Execute request!");
        }
    }
    /// <summary>
    /// Invoker Class that requires the command to execute the request
    /// </summary>
    public class Invoker
    {
        private Command command;
        public void SetCommand(Command command)
        {
            this.command = command;
        }

        public void ExecuteCommand()
        {
            command.Execute();
        }
}

Client call code

  class Program
    {
        /// <summary>
        /// Client code, create a specific command object and set its receiver
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Receiver r = new Receiver();
            Command c = new ConcreteCommand(r);
            Invoker i = new Invoker();
            i.SetCommand(c);
            i.ExecuteCommand();
            Console.Read();
        }
    }

4.3.4 scenario simulation

Go to the barbecue shop and have a kebab

4.3.5 scenario code implementation

 public abstract class Command
    {
        protected Barbecuer receiver;

        public Command(Barbecuer barbecuer)
        {
            receiver = barbecuer;
        }
        public abstract void ExcuteCommand();
    }
    /// <summary>
    /// Kebab command
    /// </summary>
    public class BakeMuttonCommand : Command
    {
        public BakeMuttonCommand(Barbecuer barbecuer) : base(barbecuer)
        {
        }

        public override void ExcuteCommand()
        {
            receiver.BakeMutton();
        }
    }

    public class BakeChickenWingCommand : Command
    {
        public BakeChickenWingCommand(Barbecuer barbecuer) : base(barbecuer)
        {
        }

        public override void ExcuteCommand()
        {
            receiver.BakeChickenWing();
        }
    }
    /// <summary>
    /// Kebab man, (receiver)
    /// </summary>
    public class Barbecuer
    {
        public void BakeMutton()
        {
            Console.WriteLine("shish kebab!");
        }

        public void BakeChickenWing()
        {
            Console.WriteLine("Roast chicken wings!");
        }
    }


    public class Waiter
    {
        private IList<Command> orders = new List<Command>();

        public void SetOrder(Command command)
        {
            if (command.ToString() == "CommandPattern2.BakeChickenWingCommand")
            {
                Console.WriteLine("Waiter: there are no chicken wings. Please order another barbecue!");
            }
            else
            {
                orders.Add(command);
                Console.WriteLine($"Add order:{command.ToString()} time:{DateTime.Now.ToString()}");
            }
        }

        public void CancelOrder(Command command)
        {
            orders.Remove(command);
            Console.WriteLine($"cancellation of order:{command.ToString()} time:{DateTime.Now.ToString()}");
        }

        /// <summary>
        /// The notice is fully implemented
        /// </summary>
        public void Notify()
        {
            foreach (Command item in orders)
            {
                item.ExcuteCommand();
            }
        }
}

Client code

 class Program
    {
        static void Main(string[] args)
        {
            Barbecuer boy = new Barbecuer();
            Command command1 = new BakeMuttonCommand(boy);
            Command command2 = new BakeMuttonCommand(boy);
            Command command3 = new BakeChickenWingCommand(boy);
            Waiter girl = new Waiter();
            //Open for business, customers order
            girl.SetOrder(command1);
            girl.SetOrder(command2);
            girl.SetOrder(command3);
            //Inform the kitchen after ordering
            girl.Notify();
            Console.Read();
        }
    }

Operation results:

4.3.6 application scenarios

The command mode can be considered in the following cases:

(1) The system needs to support Undo of commands. The command object can store the state. When the client needs to undo the effect of the command, it can call the undo method to undo the effect of the command. The command object can also provide redo method for the client to re implement the command effect when necessary.

(2) The system needs to specify and queue requests at different times. A command object and the original requester can have different lifecycles. The sender of the original request may no longer exist, and the command object itself may still be active. At this time, the receiver of the command can be local or at another address of the network. The command object can be transmitted serially to the receiver.

(3) If a system wants to update all data messages in the system to the log, so that when the system crashes, it can read back all data update commands in the log and call methods again to execute these commands one by one, so as to restore the data updates made by the system before the crash.

(4) The system needs to use command mode as an alternative to "callback" in object-oriented system. Callback means registering a method first and then calling it later.

 

Topics: C# Design Pattern