Паттерн «Команда»
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 70 из 87.
Command Pattern — это поведенческий шаблон проектирования, который преобразует запрос в самостоятельный объект, содержащий всю информацию о запросе. Это преобразование позволяет передавать запросы в качестве аргументов метода, откладывать или ставить выполнение запроса в очередь, а также поддерживать операции, которые можно отменить.
Представьте это как ресторан: вместо того чтобы напрямую говорить повару, что приготовить, вы записываете свой заказ на бланке. Официант (invoker) относит бланк (command) на кухню, где его можно поставить в очередь, расставить по приоритету или даже отменить. Этот паттерн отделяет объект, который вызывает операцию, от объекта, который её выполняет.
У этого паттерна есть четыре ключевых компонента: интерфейс Command, объявляющий метод execute, классы ConcreteCommand, реализующие конкретные actions, 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();
}
}Invoker не знает, какое действие будет выполнено. Он просто вызывает execute():
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // Вывод: Light is ONПаттерн Command особенно эффективен, когда вам нужно поставить операции в очередь, реализовать функцию отмены/повтора или запланировать задачи для последующего выполнения. Каждая команда инкапсулирует всё необходимое для выполнения действия, делая вашу систему более гибкой и расширяемой.
Задание
ЛегкоДавайте создадим систему автоматизации умного дома с использованием паттерна Command! Вы создадите систему, в которой различными домашними devices можно управлять с помощью команд, что позволяет ставить actions в очередь, выполнять их и даже сохранять для последующего использования — подобно универсальному пульту, которому не нужно знать, как работает каждое device внутри.
Вы организуете свой код в четырех файлах:
Command.java: DefineCommandinterface, который будут Implement все команды devices. Он должен Declare один methodexecute(), выполняющий action команды.Devices.java: Create два receiver класса, представляющих devices умного дома:Television— имеет methodspowerOn(), который выводитTV is now ON,powerOff(), который выводитTV is now OFF, иsetChannel(int channel), который выводитTV channel set to [channel].Thermostat— имеет methodssetTemperature(int temp), который выводитThermostat set to [temp] degrees, иturnOff(), который выводитThermostat turned OFF.Commands.java: Create Concrete command classes, которые encapsulate операции devices:TVOnCommand— принимаетTelevisionв своем constructor и вызываетpowerOn()при выполнении.TVOffCommand— принимаетTelevisionв своем constructor и вызываетpowerOff()при выполнении.TVChannelCommand— принимаетTelevisionиint channelв своем constructor и вызываетsetChannel()с этим channel при выполнении.ThermostatSetCommand— принимаетThermostatиint temperatureв своем constructor и вызываетsetTemperature()с этим значением при выполнении.Main.java: Create классRemoteControl(invoker), у которого есть methodsetCommand(Command cmd)для сохранения команды иpressButton()для выполнения сохраненной команды.В своем main method вы получите два inputs: номер канала телевизора (целое число) и температуру термостата (целое число).
Create
TelevisionиThermostat. CreateRemoteControlи Demonstrate паттерн Command следующим образом:- Установить
TVOnCommandи нажать button - Установить
TVChannelCommandс вашим input channel и нажать button - Установить
ThermostatSetCommandс вашей input temperature и нажать button - Установить
TVOffCommandи нажать button
- Установить
Вы получите два inputs по порядку: channel телевизора (целое число) и temperature термостата (целое число).
Например, при inputs 5 и 72 ваш вывод будет таким:
TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFFОбратите внимание, что RemoteControl никогда напрямую не взаимодействует с devices. Он знает только об Command interface. Каждая команда encapsulate всё необходимое для выполнения своего action, благодаря чему легко добавлять новые devices и команды, не изменяя invoker!
Попробуйте сами
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. Set a ThermostatSetCommand with the input temperature and press the button
// 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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java