← Назад к вопросам
Приведи пример использования паттерна команда
2.3 Middle🔥 171 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью#ORM и Hibernate
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Паттерн Command: Практические примеры
Паттерн Command инкапсулирует запрос как объект, позволяя параметризировать клиентов с разными запросами, ставить запросы в очередь, логировать запросы и поддерживать отмену операций. Это один из самых полезных паттернов в реальной разработке.
Суть паттерна
Комманда — это объект, который отделяет отправителя от получателя и позволяет:
- Ставить операции в очередь
- Отменять (undo) операции
- Откладывать выполнение
- Логировать все операции
Пример 1: Система команд с Undo/Redo
// Интерфейс команды
public interface Command {
void execute();
void undo();
}
// Получатель (объект, над которым выполняются действия)
public class TextEditor {
private StringBuilder text = new StringBuilder();
public void insert(String s) {
text.append(s);
}
public void delete(int count) {
text.delete(text.length() - count, text.length());
}
public String getText() {
return text.toString();
}
}
// Конкретная команда: вставка текста
public class InsertCommand implements Command {
private TextEditor editor;
private String text;
public InsertCommand(TextEditor editor, String text) {
this.editor = editor;
this.text = text;
}
@Override
public void execute() {
editor.insert(text);
}
@Override
public void undo() {
editor.delete(text.length());
}
}
// Конкретная команда: удаление текста
public class DeleteCommand implements Command {
private TextEditor editor;
private String deletedText;
private int position;
public DeleteCommand(TextEditor editor, int count) {
this.editor = editor;
this.deletedText = editor.getText().substring(
Math.max(0, editor.getText().length() - count)
);
}
@Override
public void execute() {
editor.delete(deletedText.length());
}
@Override
public void undo() {
editor.insert(deletedText);
}
}
// Инициатор (отправитель команды)
public class TextEditorInvoker {
private Deque<Command> history = new LinkedList<>();
private Deque<Command> redoStack = new LinkedList<>();
public void executeCommand(Command command) {
command.execute();
history.push(command);
redoStack.clear(); // Очищаем redo при новой команде
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo();
redoStack.push(command);
}
}
public void redo() {
if (!redoStack.isEmpty()) {
Command command = redoStack.pop();
command.execute();
history.push(command);
}
}
}
// Использование
public class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
TextEditorInvoker invoker = new TextEditorInvoker();
// Выполняем команды
invoker.executeCommand(new InsertCommand(editor, "Hello "));
invoker.executeCommand(new InsertCommand(editor, "World"));
System.out.println(editor.getText()); // Hello World
// Отмена
invoker.undo();
System.out.println(editor.getText()); // Hello
// Возврат
invoker.redo();
System.out.println(editor.getText()); // Hello World
}
}
Пример 2: Очередь команд (Task Scheduler)
// Команда для выполнения задачи
public class PrintCommand implements Command {
private String message;
public PrintCommand(String message) {
this.message = message;
}
@Override
public void execute() {
System.out.println("[" + Thread.currentThread().getName() + "] " + message);
}
@Override
public void undo() {
System.out.println("Undo: " + message);
}
}
// Планировщик команд
public class CommandScheduler {
private Queue<Command> taskQueue = new LinkedList<>();
private ExecutorService executor = Executors.newFixedThreadPool(2);
public void scheduleCommand(Command command) {
taskQueue.add(command);
}
public void executeTasks() {
while (!taskQueue.isEmpty()) {
Command command = taskQueue.poll();
executor.submit(command::execute);
}
}
public void shutdown() {
executor.shutdown();
}
}
// Использование
CommandScheduler scheduler = new CommandScheduler();
scheduler.scheduleCommand(new PrintCommand("Task 1"));
scheduler.scheduleCommand(new PrintCommand("Task 2"));
scheduler.scheduleCommand(new PrintCommand("Task 3"));
scheduler.executeTasks();
Пример 3: Система платежей
// Получатель (банк)
public class BankAccount {
private double balance = 0;
public void deposit(double amount) {
balance += amount;
System.out.println("Deposited: " + amount + ", Balance: " + balance);
}
public void withdraw(double amount) throws Exception {
if (balance >= amount) {
balance -= amount;
System.out.println("Withdrew: " + amount + ", Balance: " + balance);
} else {
throw new Exception("Insufficient funds");
}
}
public double getBalance() {
return balance;
}
}
// Команда пополнения
public class DepositCommand implements Command {
private BankAccount account;
private double amount;
public DepositCommand(BankAccount account, double amount) {
this.account = account;
this.amount = amount;
}
@Override
public void execute() {
account.deposit(amount);
}
@Override
public void undo() {
try {
account.withdraw(amount);
} catch (Exception e) {
System.out.println("Cannot undo deposit");
}
}
}
// Команда снятия
public class WithdrawCommand implements Command {
private BankAccount account;
private double amount;
public WithdrawCommand(BankAccount account, double amount) {
this.account = account;
this.amount = amount;
}
@Override
public void execute() {
try {
account.withdraw(amount);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
@Override
public void undo() {
account.deposit(amount);
}
}
// Трансакционный логгер
public class TransactionLog {
private List<Command> transactions = new ArrayList<>();
public void log(Command command) {
transactions.add(command);
}
public void printHistory() {
System.out.println("Transaction History:");
for (int i = 0; i < transactions.size(); i++) {
System.out.println((i + 1) + ". " + transactions.get(i).getClass().getSimpleName());
}
}
}
Преимущества паттерна Command
- Разделение ответственности: отправитель и получатель полностью независимы
- Undo/Redo: легко реализовать отмену операций
- Отложенное выполнение: можно ставить операции в очередь
- Логирование: все операции можно записать для аудита
- Композиция команд: можно создавать макро-команды
Реальные примеры в Java
- Spring:
@Transactionalаннотация использует Command паттерн - JavaFX:
javafx.scene.control.Buttonиспользует Command для обработки кликов - Java Concurrency:
RunnableиCallable— это те же Command - Transaction Management: системы платежей часто используют этот паттерн
Выводы
Паттерн Command — это мощный инструмент для:
- Реализации Undo/Redo функционала
- Создания очередей задач
- Логирования и аудита операций
- Отделения отправителя от получателя
Он особенно полезен в приложениях, где нужна история операций и возможность их отмены.