Шаблон проектирования команды — это поведенческий шаблон проектирования, который используется для инкапсуляции запроса в виде объекта. Это позволяет разделить задачи между объектом, который вызывает команду, и объектом, который знает, как ее выполнить. Это может быть полезно в различных ситуациях, таких как реализация функций отмены/возврата или возможность взаимозаменяемого использования различных реализаций команды. Это также может упростить создание экземпляров команд и управление ими, а также позволяет создавать составные команды, которые можно рассматривать как единое целое.

Шаблон проектирования команды — это полезный способ реализации функций отмены и повтора в приложении. Он позволяет отделить объект, вызывающий операцию, от объекта, выполняющего операцию. Это упрощает добавление новых типов операций и управление историей операций. Это также упрощает реализацию функций отмены и повтора, поскольку вы можете просто сохранить выполненные команды и воспроизвести их в обратном порядке, чтобы отменить операцию, или воспроизвести их в исходном порядке, чтобы повторить операцию. Это может быть полезно во многих различных типах приложений, таких как программы для рисования, текстовые процессоры и редакторы видеоигр.

Что командный объект сказал клиенту, который его использовал? «Я здесь, чтобы принимать ваши просьбы, а не принимать за вас решения».

Шаблон разработки команд — это способ отделить то, что мы хотим сделать, от кода, который на самом деле это делает. Представьте, что вы играете в игру со своими друзьями и хотите, чтобы персонаж-робот двигался вперед. У персонажа-робота есть куча разных частей, таких как колеса и моторы, которые должны работать вместе, чтобы заставить робота двигаться.

Шаблон разработки команд подобен дистанционному управлению роботом. На пульте дистанционного управления есть кнопка с надписью «движение вперед», и при нажатии на нее персонаж-робот будет двигаться вперед. Но важно то, что кнопка на пульте дистанционного управления на самом деле не заставляет робота двигаться  — она просто отправляет роботу команду, говорящую ему, что делать. Затем робот использует свой собственный код, чтобы выяснить, как заставить его колеса и двигатели двигаться в правильном направлении, чтобы он двигался вперед.

Это полезно, потому что это означает, что вы можете изменить движение робота, не меняя пульт дистанционного управления. Например, вы можете заставить робота двигаться быстрее или медленнее, или заставить его поворачиваться влево или вправо, вообще не меняя пульт дистанционного управления. А поскольку пульт дистанционного управления отделен от кода робота, вы можете использовать один и тот же пульт дистанционного управления для управления разными роботами, у каждого из которых свой уникальный способ передвижения.

Таким образом, шаблон проектирования команд — это способ упростить отдачу инструкций вещам, не беспокоясь о том, как они на самом деле делают то, что вы от них хотите. Это все равно, что говорить кому-то, что делать, не беспокоясь о том, как он это сделает  — вы просто даете ему команду, и он выясняет детали.

using System;
using System.Collections.Generic;

namespace CommandPatternExample
{
  // The Command interface
  interface ICommand
  {
      void Execute();
  }

  // The Invoker class
  class Switch
  {
      private List<ICommand> commands = new List<ICommand>();

      public void StoreAndExecute(ICommand command)
      {
          commands.Add(command);
          command.Execute();
      }
  }

  // The Receiver class
  class Light
  {
      public void TurnOn()
      {
          Console.WriteLine("The light is on");
      }

      public void TurnOff()
      {
          Console.WriteLine("The light is off");
      }
  }

  // The Command for turning on the light - ConcreteCommand #1 
  class FlipUpCommand : ICommand
  {
      private Light light;

      public FlipUpCommand(Light light)
      {
          this.light = light;
      }

      public void Execute()
      {
          light.TurnOn();
      }
  }

  // The Command for turning off the light - ConcreteCommand #2
  class FlipDownCommand : ICommand
  {
      private Light light;

      public FlipDownCommand(Light light)
      {
          this.light = light;
      }

      public void Execute()
      {
          light.TurnOff();
      }
  }

  class Program
  {
      static void Main(string[] args)
      {
          Light lamp = new Light();

          ICommand switchUp = new FlipUpCommand(lamp);
          ICommand switchDown = new FlipDownCommand(lamp);

          Switch s = new Switch();

          // Invoking the switchOn() method on switchUp 
          s.StoreAndExecute(switchUp);

          // Invoking the switchOff() method on switchDown
          s.StoreAndExecute(switchDown);

          // You could continue the code by adding additional commands and executing them using the Switch class, 
          // for example:
          ICommand switchDim = new DimCommand(lamp);
          s.StoreAndExecute(switchDim);

          // Or you could add a new Receiver class and create commands for it:
          Fan fan = new Fan();
          ICommand fanOn = new FanOnCommand(fan);
          ICommand fanOff = new FanOffCommand(fan);

          s.StoreAndExecute(fanOn);
          s.StoreAndExecute(fanOff);
    }
}

Чтобы отменить операцию, вы можете добавить новый метод Undo в интерфейс ICommand, который будет вызываться классом Switch для отмены последней операции. операция. Вы можете реализовать это, добавив в класс Switch стек, в котором будут храниться выполняемые команды. Затем метод Undo может извлечь последнюю команду из стека и выполнить собственную операцию отмены.

// The Command interface
interface ICommand
{
 void Execute();
 void Undo();
}


// The Invoker class
class Switch
{
  // converted to Stack from List because of using Pop method.
    private Stack<ICommand> commands = new Stack<ICommand>();

    public void StoreAndExecute(ICommand command)
    {
        commands.Push(command);
        command.Execute();
    }

    public void UndoLast()
    {
        if (commands.Count > 0)
        {
            // pop is the method of Stack class
            // which return the last object and remove it from the list.
            ICommand command = commands.Pop();
            command.Undo();
        }
    }
}

Затем вы можете добавить методы Undo к конкретным объектам ICommand.

// The Command for turning on the light - ConcreteCommand #1 
class FlipUpCommand : ICommand
{
    private Light light;

    public FlipUpCommand(Light light)
    {
        this.light = light;
    }

    public void Execute()
    {
        light.TurnOn();
    }

    public void Undo()
  {
    light.TurnOff();
  }
}

/ The Command for turning off the light - ConcreteCommand #2
class FlipDownCommand : ICommand
{
  private Light light;
  public FlipDownCommand(Light light)
  {
       this.light = light;
  }

  public void Execute()
  {
       light.TurnOff();
  }

  public void Undo()
  {
       light.TurnOn();
  }
}

Теперь вы можете бесплатно отменить последнюю операцию.

ICommand flipUp = new FlipUpCommand(light);
ICommand flipDown = new FlipDownCommand(light);

lightSwitch.StoreAndExecute(flipUp);
lightSwitch.StoreAndExecute(flipDown);

lightSwitch.UndoLast();
lightSwitch.UndoLast();

Что командный объект сказал клиенту, когда его попросили выполнить операцию? — Я просто выполняю приказ.

Одним из распространенных вариантов использования шаблона проектирования Command является реализация функции отмены/повтора в приложении. Инкапсулируя каждое действие в виде командного объекта, легко вести историю операций и позволять пользователю отменять и повторять действия. Это может быть особенно полезно в приложениях, которые включают сложные или необратимые действия, такие как финансовые транзакции или редактирование документов.

Еще один вариант использования шаблона проектирования Command — реализовать отложенное выполнение операций. В некоторых сценариях стоимость создания объекта команды может быть высокой, и выполнение операции может быть не всегда необходимо. Инкапсулируя операцию в виде командного объекта, можно отложить выполнение до тех пор, пока оно не понадобится, что поможет повысить производительность и эффективность приложения.

Шаблон разработки Command также можно использовать для реализации макросов, представляющих собой последовательности связанных действий, которые можно рассматривать как единое целое и выполнять с помощью одной команды. Это может быть полезно для автоматизации повторяющихся задач или для создания сложных операций, включающих несколько шагов.

Схема UML

На этой диаграмме класс Command определяет интерфейс для выполнения действия, а классы ConcreteCommand реализуют этот интерфейс для выполнения действия над объектом Receiver. Класс Invoker содержит объект Command и вызывает метод execute команды.

Заключение

Шаблон проектирования команды полезен в ситуациях, когда вы хотите отделить объект, который вызывает действие (вызывающий), от объекта, который выполняет действие (получатель), инкапсулируя запрос действия в объект (команду). Это позволяет вам ставить в очередь или регистрировать запросы, а также поддерживать действия, которые нельзя отменить. Примеры того, когда вы можете использовать шаблон команды, включают реализацию функций отмены/возврата в текстовом редакторе или редакторе документов, реализацию пульта дистанционного управления с кнопками включения/выключения для телевизора или другого устройства, а также реализацию системы макросов, которая позволяет пользователю записывать и воспроизвести последовательность действий.

Вот несколько примеров того, когда вы можете использовать шаблон проектирования команды:

- Реализация функции отмены/повтора в текстовом редакторе или редакторе документов. Инициатором в этом случае будет функция отмены/возврата, а получателем будет текстовый редактор или редактор документов. Команды будут представлять различные действия, которые можно выполнять с документом, такие как вставка или удаление текста, форматирование текста или перемещение объектов.

- Реализация пульта дистанционного управления с кнопками включения/выключения для телевизора или другого устройства. Вызывающим в этом случае будет пульт дистанционного управления, а приемником будет телевизор или другое устройство. Команды будут представлять различные действия, которые можно выполнять на устройстве, например включение или выключение, регулировка громкости или изменение канала.

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

- Реализация игры с историей ходов, которые можно отменить и переделать. Инициатором в этом случае будет функция отмены/возврата, а получателем будет игра. Команды будут представлять различные ходы, которые могут быть выполнены в игре, например, перемещение фигуры по доске или выполнение действия в ролевой игре.

Во всех этих примерах шаблон проектирования команды позволяет отделить инициатора от получателя, упрощая реализацию функций отмены/возврата, постановки в очередь или регистрации запросов или поддержки других сценариев, в которых вам необходимо манипулировать последовательностью действий.

Вот последний пример, в котором вы можете использовать шаблон проектирования команды:

public interface ICommand
{
    void Execute();
}

public class ConcreteCommand : ICommand
{
    private Receiver _receiver;

    public ConcreteCommand(Receiver receiver)
    {
        _receiver = receiver;
    }

    public void Execute()
    {
        _receiver.Action();
    }
}

public class Receiver
{
    public void Action()
    {
        Console.WriteLine("Called Receiver.Action()");
    }
}

public class Invoker
{
    private ICommand _command;

    public void SetCommand(ICommand command)
    {
        _command = command;
    }

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

class Program
{
    static void Main(string[] args)
    {
        Receiver receiver = new Receiver();
        ICommand command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();

        invoker.SetCommand(command);
        invoker.ExecuteCommand();

        Console.ReadKey();
    }
}

Как видите, в этом примере нет функции отмены. Возможно, вы сможете изменить этот пример и добавить функцию Отменить.

Спасибо за прочтение! Если вы нашли статью полезной, вы можете аплодировать и подписаться. Таким образом, вы будете получать уведомления о новых статьях.

# ссылки

Он был создан с помощью ChatGPT AI.