الگوی Command

الگوی Command(فرمان) در دسته الگوهای رفتاری قرار دارد. یک درخواست را به عنوان یک شیء کپسوله میکند، در نتیجه اجازه می دهد client ها را با درخواست های مختلف تنظیم کنید(parameterize - پارامتری کردن) ، صف یا درخواست لاگ، و پشتیبانی از عملیات قابل لغو(undoable operations). به عبارت دیگر فاصله انداختن بین درخواست دهنده و گیرنده درخواست با ذخیره کردن درخواست ها به شکل شی و فراهم کردن قابلیت هایی مانند Undo،Redo، لاگ گیری از فعالیتهای کاربر، اسکریپت نویسی و مانند آن. UML class diagram شركت كنندگان کلاس ها و اشیاء شرکت کننده در این الگو عبارتند از: فرمان (در مثال دنیای واقعی Command (Command : یک واسط برای اجرای عملیات را اعلام می کند.( کلاس مجرد یا واسطی است که ساختار کلی فرمانها را تعریف می کند. )فرمان سفت ConcreteCommand (فرمان ماشین حساب CalculatorCommand): یک اتصال ( binding ) بین یک شی گیرنده و یک عمل تعریف می کند.پیاده سازی اجرا با فراخوانی عملیات (ها) متناظردر گیرنده به عبارت دیگر کلاس هایی هستند که واسط Command را پیاده سازی کرده و هر کدام آنها معادل یک فرمان در برنامه است. هر یک از این کلاس ها مشخصات درخواست،وضعیت برنامه و گیرنده واقعی درخواست را در خود( درون فیلد state و receiver) نگهداری می کند. کلاینت Client (اپ فرمان CommandApp): یک شی ConcreteCommand ایجاد می کند و گیرنده آن را تنظیم(set) می کند.(کسی است که اشیاء Command را ایجاد می کند. )فراخوانی کننده Invoker (کاربر User): از فرمان می پرسد تا درخواست را انجام دهد.به عبارت دیگر کسی است که با در دست داشتن یک شی فرمان می‌تواند (با فراخوانی متد Execute) از او بخواهد عملیاتش را اجرا کند( به طور غیر مستقیم ازReceiver بخواهد کار خود را انجام بدهد).این کلاس (Invoker) دو حالت دارد:فراخوانی ساده SimpleInvoker : مولفه ای است که یک فرمان پایه ای مشخص به او نسبت داده می شود(مانند گزینه های منو).فراخوانی پیچیده ComplexInvoker : یک مولفه ی پیچیده است که فرمان او از ترکیب قرمان های پایه ای به دست آمده یا عمل Rendo یا Undo، اجرای ماکرو و مانند آن به او سپرده میشود.ساختار کلی کد این کد ساختاری، الگوی فرمان را نشان می دهد که درخواست های ذخیره شده را به عنوان اشیایی که کلاینت ها را قادر می سازد تا درخواست ها را اجرا (execute) یا بازپخش (playback) کنند، ذخیره کند. using System; namespace DoFactory.GangOfFour.Command.Structural { /// <summary> /// MainApp startup class for Structural /// Command Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { // Create receiver, command, and invoker Receiver receiver = new Receiver (); Command command = new ConcreteCommand (receiver); Invoker invoker = new Invoker (); // Set and execute command invoker.SetCommand (command); invoker.ExecuteCommand (); // Wait for user Console.ReadKey (); } } /// <summary> /// The 'Command' abstract class /// </summary> abstract class Command { protected Receiver receiver; // Constructor public Command (Receiver receiver) { this.receiver = receiver; } public abstract void Execute (); } /// <summary> /// The 'ConcreteCommand' class /// </summary> class ConcreteCommand : Command { // Constructor public ConcreteCommand (Receiver receiver): base (receiver) { } public override void Execute () { receiver.Action (); } } /// <summary> /// The 'Receiver' class /// </summary> class Receiver { public void Action () { Console.WriteLine ("Called Receiver.Action()"); } } /// <summary> /// The 'Invoker' class /// </summary> class Invoker { private Command _command; public void SetCommand (Command command) { this._command = command; } public void ExecuteCommand () { _command.Execute (); } } } OutputCalled Receiver.Action ()یاpublic static class ServiceLocator { private readonly static Dictionary<Type, object> services = new Dictionary<Type, object>(); public static T GetService<T>() { return (T)ServiceLocator.services[typeof(T)]; } public static void Register<T>(T service) { ServiceLocator.services[typeof(T)] = service; } public static void Reset() { ServiceLocator.services.Clear(); } } interface IReceiver1 { void Action1(); } class Receiver1: IReceiver1 { public void Action1() { Console.WriteLine("Receiver1.Action1()"); } } interface IReceiver2 { void Action2(); } class Receiver2: IReceiver2 { public void Action2() { Console.WriteLine("Receiver2.Action2()"); } } abstract class Command { public abstract void Execute(); } class ConcreteCommand1: Command { private IReceiver1 receiver; public ConcreteCommand1(IReceiver1 receiver) { this.receiver = receiver; } public override void Execute() { Console.WriteLine("ConcreteCommand1.Execute()"); receiver.Action1(); } } class ConcreteCommand2: Command { public override void Execute() { Console.WriteLine("ConcreteCommand2.Execute()"); var receiver = ServiceLocator.GetService<IReceiver2>(); DoSomething(); receiver.Action2(); } private void DoSomething() { Console.WriteLine("ConcreteCommand2.DoSomething()"); } } class SimpleInvoker { private Command command; public SimpleInvoker(Command command) { this.command = command; } public void Consume() { Console.WriteLine("\nSimpleInvoker.Consume()"); command.Execute(); } } class ComplexInvoker { private List<Command> commands; public ComplexInvoker() { commands = new List<Command>(); } public void AddCommand(Command command) { commands.Add(command); } public void Play() { Console.WriteLine("\nComplexInvoker.Play()"); foreach (var command in commands) { command.Execute(); } } } class Test { static void Main() { ServiceLocator.Register<IReceiver1>(new Receiver1()); ServiceLocator.Register<IReceiver2>(new Receiver2()); var receiver1 = ServiceLocator.GetService<IReceiver1>(); var cmd1 = new ConcreteCommand1(receiver1); var invoker1 = new SimpleInvoker(cmd1); invoker1.Consume(); var invoker2 = new ComplexInvoker(); invoker2.AddCommand(new ConcreteCommand1(receiver1)); invoker2.AddCommand(new ConcreteCommand2()); invoker2.Play(); Console.ReadKey(); } } /* OUTPUT:SimpleInvoker.Consume()ConcreteCommand1.Execute()Receiver1.Action1()ComplexInvoker.Play()ConcreteCommand1.Execute()Receiver1.Action1()ConcreteCommand2.Execute()ConcreteCommand2.DoSomething()Receiver2.Action2()*/مثال دنیای واقعی نشان می دهد الگوی فرماندهی (فرمان) مورد استفاده در یک ماشین حساب ساده با تعداد نامحدود از undo و redo است. توجه داشته باشید که در C # کلمه " operator " کلمه کلیدی است. پیشوند آن با '@' امکان استفاده از آن را به عنوان یک شناسه می دهد. using System; using System.Collections.Generic; namespace DoFactory.GangOfFour.Command.RealWorld { /// <summary> /// MainApp startup class for Real-World /// Command Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { // Create user and let her compute User user = new User (); // User presses calculator buttons user.Compute ('+', 100); user.Compute ('-', 50); user.Compute ('*', 10); user.Compute ('/', 2); // Undo 4 commands user.Undo (4); // Redo 3 commands user.Redo (3); // Wait for user Console.ReadKey (); } } /// <summary> /// The 'Command' abstract class /// </summary> abstract class Command { public abstract void Execute (); public abstract void UnExecute (); } /// <summary> /// The 'ConcreteCommand' class /// </summary> class CalculatorCommand : Command { private char _operator; private int _operand; private Calculator _calculator; // Constructor public CalculatorCommand (Calculator calculator, char @operator, int operand) { this._calculator = calculator; this._operator = @operator; this._operand = operand; } // Gets operator public char Operator { set { _operator = value; } } // Get operand public int Operand { set { _operand = value; } } // Execute new command public override void Execute () { _calculator.Operation (_operator, _operand); } // Unexecute last command public override void UnExecute () { _calculator.Operation (Undo (_operator), _operand); } // Returns opposite operator for given operator private char Undo (char @operator) { switch (@operator) { case '+': return '-'; case '-': return '+'; case '*': return '/'; case '/': return '*'; default: throw new ArgumentException ("@operator"); } } } /// <summary> /// The 'Receiver' class /// </summary> class Calculator { private int _curr = 0; public void Operation (char @operator, int operand) { switch (@operator) { case '+': _curr += operand; break; case '-': _curr -= operand; break; case '*': _curr *= operand; break; case '/': _curr /= operand; break; } Console.WriteLine ( "Current value = {0,3} (following {1} {2})", _curr, @operator, operand); } } /// <summary> /// The 'Invoker' class /// </summary> class User { // Initializers private Calculator _calculator = new Calculator (); private List<Command> _commands = new List<Command> (); private int _current = 0; public void Redo (int levels) { Console.WriteLine ("\n---- Redo {0} levels ", levels); // Perform redo operations for (int i = 0; i < levels; i++) { if (_current < _commands.Count - 1) { Command command = _commands[_current++]; command.Execute (); } } } public void Undo (int levels) { Console.WriteLine ("\n---- Undo {0} levels ", levels); // Perform undo operations for (int i = 0; i < levels; i++) { if (_current > 0) { Command command = _commands[--_current] as Command; command.UnExecute (); } } } public void Compute (char @operator, int operand) { // Create command operation and execute it Command command = new CalculatorCommand ( _calculator, @operator, operand); command.Execute (); // Add command to undo list _commands.Add (command); _current++; } } } OutputCurrent value = 100 (following + 100)Current value = 50 (following - 50)Current value = 500 (following * 10)Current value = 250 (following / 2) -- --Undo 4 levelsCurrent value = 500 (following * 2)Current value = 50 (following / 10)Current value = 100 (following + 50)Current value = 0 (following - 100) -- --Redo 3 levelsCurrent value = 100 (following + 100)Current value = 50 (following - 50)Current value = 500 (following * 10)منبع