Что такое паттерн Command (Команда)?

Ответ

Паттерн Command (Команда) — это поведенческий паттерн проектирования, который инкапсулирует запрос в виде объекта. Это позволяет параметризовать клиентов с различными запросами, ставить запросы в очередь, логировать их, а также поддерживать отмену операций.

Зачем он нужен? Чтобы отделить объект, инициирующий операцию (Invoker), от объекта, который знает, как её выполнить (Receiver). Это обеспечивает слабую связность и добавляет возможности управления запросами.

Ключевые компоненты:

  • Command: Интерфейс с методом execute() (и часто undo()).
  • ConcreteCommand: Конкретная реализация команды. Связывает действие (Receiver) с параметрами.
  • Invoker: Инициирует выполнение команды (например, кнопка в UI).
  • Receiver: Объект, который знает, как выполнить саму операцию.
  • Client: Создаёт и настраивает конкретную команду.

Пример на Java (с поддержкой отмены):

// 1. Интерфейс команды
interface Command {
    void execute();
    void undo();
}

// 2. Receiver (Получатель) - знает, как выполнить действие
class Light {
    public void turnOn() { System.out.println("Свет включён"); }
    public void turnOff() { System.out.println("Свет выключен"); }
}

// 3. ConcreteCommand (Конкретная команда)
class TurnOnLightCommand implements Command {
    private Light light;

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

    @Override
    public void execute() {
        light.turnOn();
    }

    @Override
    public void undo() {
        light.turnOff();
    }
}

// 4. Invoker (Инициатор) - хранит и выполняет команду
class RemoteControl {
    private Command command;
    private Command lastCommand; // Для отмены

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        if (command != null) {
            lastCommand = command;
            command.execute();
        }
    }

    public void pressUndo() {
        if (lastCommand != null) {
            lastCommand.undo();
            lastCommand = null;
        }
    }
}

// 5. Client (Клиент) - настраивает команды
public class Main {
    public static void main(String[] args) {
        Light livingRoomLight = new Light(); // Receiver
        Command turnOn = new TurnOnLightCommand(livingRoomLight); // ConcreteCommand

        RemoteControl remote = new RemoteControl(); // Invoker
        remote.setCommand(turnOn);

        remote.pressButton(); // Вывод: Свет включён
        remote.pressUndo();   // Вывод: Свет выключен
    }
}

Преимущества:

  • Отделение отправителя и получателя: Уменьшает связность.
  • Реализация отмены/повтора операций (Undo/Redo).
  • Возможность создания составных команд (макросов).
  • Постановка команд в очередь или их логирование.

Недостатки:

  • Усложняет код из-за введения множества дополнительных классов.

Ответ 18+ 🔞

Так, слушай, смотри, есть у нас в программировании такая хуйня, которая называется Паттерн "Команда". Это, блядь, не приказ какой-то, а такой поведенческий трюк, когда ты заворачиваешь какой-то запрос или действие в отдельный объект, как в конверт. Получается такая инкапсуляция, ёпта!

А нахуя это надо, спросишь? А затем, чтобы тот, кто команду даёт (ну, типа кнопка на пульте), нихрена не знал про того, кто её выполняет (лампочка там, телевизор). Они друг про друга не в курсе, связь слабая — красота! Зато можно команды в очередь ставить, логировать, а главное — отменять, если накосячил. Овердохуище возможностей открывается.

Из каких деталей эта хуйня собирается?

  • Command (Команда): Просто интерфейс, который говорит: «У любой команды должен быть метод execute() (выполнить) и, желательно, undo() (отменить)». Всё, больше от неё нихуя не требуется.
  • ConcreteCommand (Конкретная команда): А вот это уже реальная пацанская команда. Она знает, кого дергать (Receiver) и что ему сказать. Связующее звено, блядь.
  • Invoker (Инициатор): Это и есть наш пульт, кнопка, менюшка. Его задача — крикнуть команде «Выполняйся!». Сам он нихуя не делает.
  • Receiver (Получатель): А это тот самый работяга, который знает, как конкретно свет включить или музыку прибавить. Объект с реальной логикой.
  • Client (Клиент): Это мы с тобой, которые всё это хозяйство собирают: создают лампочку, команду для неё, и суют эту команду в пульт.

Смотри, как это в коде выглядит, на примере света с отменой:

// 1. Интерфейс команды - контракт для всех
interface Command {
    void execute(); // Сделай!
    void undo();    // Отмени! (Не всегда, но тут есть)
}

// 2. Receiver (Получатель) - тот, кто реально работает
class Light {
    public void turnOn() { System.out.println("Свет включён"); }
    public void turnOff() { System.out.println("Свет выключен"); }
}

// 3. ConcreteCommand (Конкретная команда) - "Включи свет"
class TurnOnLightCommand implements Command {
    private Light light; // Она знает, какую именно лампу дергать

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

    @Override
    public void execute() {
        light.turnOn(); // Дёрнула получателя
    }

    @Override
    public void undo() {
        light.turnOff(); // И отменить умеет!
    }
}

// 4. Invoker (Инициатор) - наш пульт
class RemoteControl {
    private Command command;
    private Command lastCommand; // Запомним последнюю, чтобы отменить

    public void setCommand(Command command) {
        this.command = command; // Вставили в пульт нужную команду
    }

    public void pressButton() {
        if (command != null) {
            lastCommand = command; // Запомнили
            command.execute();     // Выполнили
        }
    }

    public void pressUndo() {
        if (lastCommand != null) {
            lastCommand.undo(); // Откатили
            lastCommand = null;
        }
    }
}

// 5. Client (Клиент) - собираем конструктор
public class Main {
    public static void main(String[] args) {
        Light livingRoomLight = new Light(); // Создали лампу (Receiver)
        Command turnOn = new TurnOnLightCommand(livingRoomLight); // Создали команду для неё

        RemoteControl remote = new RemoteControl(); // Взяли пульт (Invoker)
        remote.setCommand(turnOn); // Засунули команду в пульт

        remote.pressButton(); // Жмём! -> "Свет включён"
        remote.pressUndo();   // Ой, передумал! -> "Свет выключен"
    }
}

Что хорошего?

  • Развязал всем руки: Отправитель команды и исполнитель теперь не знают друг о друге. Слабая связность — залог гибкости, блядь.
  • Отмена/Повтор (Undo/Redo) реализуется почти на раз-два.
  • Можно сложные команды делать из простых (макросы).
  • Команды можно в очередь складывать или логировать — вообще красота.

Что плохого?

  • Классов становится, как говна за баней. На каждую мелкую операцию — свой класс команды. Может перегрузить проект, если им не злоупотреблять.