Паттерн «Команда»
Часть раздела Объектно-ориентированное программирование путешествия по 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) или планировать задачи для последующего выполнения. Каждая команда инкапсулирует всё необходимое для выполнения действия, что делает вашу систему более гибкой и расширяемой.
Задание
ЛегкоДавайте создадим систему автоматизации умного дома, используя паттерн Команда (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и продемонстрируйте работу паттерна Команда:- Установите
TVOnCommandи нажмите кнопку - Установите
TVChannelCommandс введенным номером канала и нажмите кнопку - Установите
ThermostatSetCommandс введенной температурой и нажмите кнопку - Установите
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();
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»