Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 70 из 87.

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

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

Паттерн состоит из четырех ключевых компонентов: интерфейс Command, объявляющий метод execute, классы ConcreteCommand, реализующие конкретные действия, Receiver, который выполняет фактическую работу, и Invoker, который запускает команды:

interface Command {
    void execute();
}
class Light {  // Получатель
    public void turnOn() {
        System.out.println("Light is ON");
    }
    public void turnOff() {
        System.out.println("Light is OFF");
    }
}
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    public void execute() {
        light.turnOn();
    }
}
class RemoteControl {  // Инициатор (Invoker)
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
    }
}

Вызывающий объект не знает, какое действие будет выполнено — он просто вызывает execute():

Light light = new Light();
Command lightOn = new LightOnCommand(light);

RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();  // Вывод: Свет включен

Паттерн «Команда» (Command Pattern) отлично подходит в тех случаях, когда вам нужно ставить операции в очередь, реализовать функциональность отмены и повтора действий (undo/redo) или планировать задачи для последующего выполнения. Каждая команда инкапсулирует всё необходимое для выполнения действия, что делает вашу систему более гибкой и расширяемой.

challenge icon

Задание

Легко

Давайте создадим систему автоматизации умного дома, используя паттерн Команда (Command Pattern)! Вы создадите систему, в которой различными домашними устройствами можно управлять с помощью команд, что позволяет ставить действия в очередь, выполнять их и даже сохранять для последующего использования — прямо как универсальный пульт дистанционного управления, которому не нужно знать, как каждое устройство работает внутри.

Вы организуете свой код в четырех файлах:

  • Command.java: Определите интерфейс Command, который будут реализовывать все команды устройств. Он должен объявлять один метод execute(), который выполняет действие команды.
  • Devices.java: Создайте два класса-получателя (receiver), представляющих устройства умного дома:

    Television — имеет методы powerOn(), который выводит TV is now ON, powerOff(), который выводит TV is now OFF, и setChannel(int channel), который выводит TV channel set to [channel].

    Thermostat — имеет методы setTemperature(int temp), который выводит Thermostat set to [temp] degrees, и turnOff(), который выводит Thermostat turned OFF.

  • Commands.java: Создайте конкретные классы команд, которые инкапсулируют операции устройств:

    TVOnCommand — принимает Television в конструкторе и вызывает powerOn() при выполнении.

    TVOffCommand — принимает Television в конструкторе и вызывает powerOff() при выполнении.

    TVChannelCommand — принимает Television и int channel в конструкторе и вызывает setChannel() с этим каналом при выполнении.

    ThermostatSetCommand — принимает Thermostat и int temperature в конструкторе и вызывает setTemperature() с этим значением при выполнении.

  • Main.java: Создайте класс RemoteControl (инициатор/invoker), который имеет метод setCommand(Command cmd) для хранения команды и pressButton() для выполнения сохраненной команды.

    В методе main вы получите два входных значения: номер ТВ-канала (целое число) и температуру термостата (целое число).

    Создайте объекты Television и Thermostat. Создайте RemoteControl и продемонстрируйте работу паттерна Команда:

    1. Установите TVOnCommand и нажмите кнопку
    2. Установите TVChannelCommand с введенным номером канала и нажмите кнопку
    3. Установите ThermostatSetCommand с введенной температурой и нажмите кнопку
    4. Установите TVOffCommand и нажмите кнопку

Вы получите два входных значения по порядку: ТВ-канал (целое число) и температуру термостата (целое число).

Например, при входных данных 5 и 72 ваш вывод будет следующим:

TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFF

Обратите внимание, что RemoteControl никогда не взаимодействует с устройствами напрямую — он знает только об интерфейсе Command. Каждая команда инкапсулирует все необходимое для выполнения своего действия, что позволяет легко добавлять новые устройства и команды без изменения инициатора!

Шпаргалка

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

Этот паттерн отделяет объект, инициирующий операцию (отправитель), от объекта, который непосредственно её выполняет (получатель).

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

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

Базовая структура

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

// Получатель
class Light {
    public void turnOn() {
        System.out.println("Light is ON");
    }
    public void turnOff() {
        System.out.println("Light is OFF");
    }
}

// Конкретная команда
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    public void execute() {
        light.turnOn();
    }
}

// Отправитель
class RemoteControl {
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
    }
}

Использование

Light light = new Light();
Command lightOn = new LightOnCommand(light);

RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();  // Вывод: Light is ON

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

Попробуйте сами

import java.util.Scanner;

// TODO: Создайте класс RemoteControl (инициатор) с:
// - методом setCommand(Command cmd) для хранения команды
// - методом pressButton() для выполнения сохраненной команды

class RemoteControl {
    // TODO: Реализуйте RemoteControl
}

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Считывание входных данных
        int channel = scanner.nextInt();
        int temperature = scanner.nextInt();
        
        // TODO: Создайте Television и Thermostat
        
        // TODO: Создайте RemoteControl
        
        // TODO: Продемонстрируйте паттерн Command:
        // 1. Установите TVOnCommand и нажмите кнопку
        // 2. Установите TVChannelCommand с введенным каналом и нажмите кнопку
        // 3. Установите ThermostatSetCommand с введенной температурой и нажмите кнопку
        // 4. Установите TVOffCommand и нажмите кнопку
        
        scanner.close();
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование