1 10.3K ru

Паттерн «Команда» (Command)

Categories: 💻 Programming

Назначение: инкапсулирует запрос как объект, позволяя тем самым задавать параметры клиентов для обработки соответствующих запросов, ставить запросы в очередь или протоколировать их, а также поддерживать отмену операций.
Паттерн «Команда» позволяет спрятать действие в объекте и отвязать источник этого действия от места его исполнения. Классический пример — проектирование пользовательского интерфейса. Пункт меню не должен знать, что происходит при его активизации пользователем, он должен знать лишь о некотором действии, которое нужно выполнить при нажатии кнопки.

Когда использовать Паттерн Command

  • Когда необходимо обеспечить выполнение очереди запросов, а также их возможную отмену.
  • Когда надо поддерживать логгирование изменений в результате запросов. Использование логов может помочь восстановить состояние системы - для этого необходимо будет использовать последовательность запротоколированных команд.
  • Когда необходимо параметризировать объекты выполняемым действием, ставить запросы в очередь или поддерживать операции отмены (undo) и повтора (redo) действий.

UML схема паттерна "Команда":

command uml schema

Реализация шаблона "команда" на C#

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();
    }
  }
}

Примеры в .NET Framework

  • ICommand в WPF, на основе которых строится привязка операций к событиям пользовательского интерфейса;
  • IDbCommand в ADO.NET инкапсулирует операцию, исполняемую на стороне СУБД; 
  • Объект класса Task<T> принимает делегат Func<T>, который можно рассматривать в виде команды, которая будет исполнена в будущем для получения результата задачи.

Comments:

Please log in to be able add comments.
👍