Командный паттерн (Command Pattern) является мощным инструментом для организации обработки действий в приложениях на JavaScript, особенно в фреймворках с компонентной архитектурой, таких как Riot.js. Этот паттерн позволяет инкапсулировать запросы как объекты, делая их независимыми от вызывающих и выполняющих сторон, что повышает гибкость и расширяемость кода.
Командный паттерн отделяет отправителя команды от получателя, позволяя:
В контексте Riot.js команды часто применяются для обработки пользовательских событий, сетевых запросов или взаимодействия между компонентами.
Стандартная структура командного паттерна включает три ключевых элемента:
execute() с определённой логикой действия.Пример базовой структуры на Jav * aScript:
class Command {
execute() {
throw new Error("Метод execute() должен быть реализован");
}
}
class Receiver {
doAction(data) {
console.log(`Выполнение действия с данными: ${data}`);
}
}
class ConcreteCommand extends Command {
constructor(receiver, payload) {
super();
this.receiver = receiver;
this.payload = payload;
}
execute() {
this.receiver.doAction(this.payload);
}
}
class Invoker {
constructor() {
this.commands = [];
}
storeCommand(command) {
this.commands.push(command);
}
runCommands() {
this.commands.forEach(command => command.execute());
this.commands = [];
}
}
// Пример использования
const receiver = new Receiver();
const command1 = new ConcreteCommand(receiver, "Данные 1");
const command2 = new ConcreteCommand(receiver, "Данные 2");
const invoker = new Invoker();
invoker.storeCommand(command1);
invoker.storeCommand(command2);
invoker.runCommands();
В Riot.js компоненты являются естественными получателями команд, а события пользовательского интерфейса могут выступать в роли отправителей. Например, обработка клика кнопки:
<counter>
<p>Счётчик: { state.count }</p>
<button oncl ick={increment}>Увеличить</button>
<script>
import { CounterCommand } from './commands.js';
export default {
state: { count: 0 },
increment() {
const command = new CounterCommand(this, 1);
command.execute();
}
}
</script>
</counter>
Файл commands.js может содержать инкапсуляцию
логики:
export class CounterCommand {
constructor(component, value) {
this.component = component;
this.value = value;
}
execute() {
this.component.state.count += this.value;
this.component.update();
}
}
Такое разделение позволяет хранить бизнес-логику отдельно от компонентов и легко расширять функционал без изменения исходного компонента.
Командный паттерн удобно использовать с очередями и отменой действий, что особенно полезно для сложных интерфейсов:
class CommandQueue {
constructor() {
this.queue = [];
this.history = [];
}
add(command) {
this.queue.push(command);
}
execute() {
while (this.queue.length) {
const command = this.queue.shift();
command.execute();
this.history.push(command);
}
}
undo() {
const command = this.history.pop();
if (command && command.undo) {
command.undo();
}
}
}
Пример расширения команды с возможностью отката:
export class CounterCommand {
constructor(component, value) {
this.component = component;
this.value = value;
}
execute() {
this.component.state.count += this.value;
this.component.update();
}
undo() {
this.component.state.count -= this.value;
this.component.update();
}
}
Использование очереди позволяет создавать цепочки команд и управлять состоянием приложения более гибко:
const queue = new CommandQueue();
queue.add(new CounterCommand(counterComponent, 1));
queue.add(new CounterCommand(counterComponent, 2));
queue.execute();
queue.undo(); // откат последней команды
Командный паттерн особенно эффективен в крупных приложениях на Riot.js, где требуется централизованное управление действиями и предсказуемость состояния. Он гармонично сочетается с реактивной природой фреймворка, позволяя строить чистую, модульную архитектуру.