Patrón Command
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 70 de 87.
El Patrón Command es un patrón de diseño de comportamiento que convierte una solicitud en un objeto independiente que contiene toda la información sobre la solicitud. Esta transformación le permite pasar solicitudes como argumentos de método, retrasar o poner en cola la ejecución de una solicitud y admitir operaciones que se pueden deshacer.
Piénsalo como un restaurante: en lugar de decirle directamente al chef qué cocinar, escribes tu pedido en una nota. El camarero (invoker) lleva la nota (command) a la cocina, donde puede ser encolada, priorizada o incluso cancelada. El patrón desacopla el objeto que invoca la operación del que la realiza.
El patrón tiene cuatro componentes clave: una interfaz Command que declara el método execute, clases ConcreteCommand que implementan acciones específicas, un Receiver que realiza el trabajo real y un Invoker que activa los comandos:
interface Command {
void execute();
}class Light { // Receptor
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 { // Invocador
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}El invocador no sabe qué acción se realizará; simplemente llama a execute():
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // Salida: La luz está encendidaEl patrón Command sobresale cuando se necesita encolar operaciones, implementar la funcionalidad de deshacer/rehacer o programar tareas para su ejecución posterior. Cada comando encapsula todo lo necesario para realizar una acción, lo que hace que su sistema sea más flexible y extensible.
Desafío
Fácil¡Vamos a construir un sistema de automatización del hogar inteligente utilizando el Patrón Command! Crearás un sistema donde varios dispositivos del hogar pueden ser controlados a través de comandos, permitiendo que las acciones sean puestas en cola, ejecutadas e incluso almacenadas para su uso posterior, tal como un control remoto universal que no necesita saber cómo funciona internamente cada dispositivo.
Organizarás tu código en cuatro archivos:
Command.java: Define la interfazCommandque implementarán todos los comandos de los dispositivos. Debe declarar un único métodoexecute()que realice la acción del comando.Devices.java: Crea dos clases receptoras que representen dispositivos del hogar inteligente:Television- tiene los métodospowerOn()que imprime"TV is now ON",powerOff()que imprime"TV is now OFF", ysetChannel(int channel)que imprime"TV channel set to " + channel.Thermostat- tiene los métodossetTemperature(int temp)que imprime"Thermostat set to " + temp + " degrees"yturnOff()que imprime"Thermostat turned OFF".Commands.java: Crea clases de comandos concretos que encapsulen las operaciones de los dispositivos:TVOnCommand- recibe unaTelevisionen su constructor y llama apowerOn()cuando se ejecuta.TVOffCommand- recibe unaTelevisionen su constructor y llama apowerOff()cuando se ejecuta.TVChannelCommand- recibe unaTelevisiony unint channelen su constructor, y llama asetChannel()con ese canal cuando se ejecuta.ThermostatSetCommand- recibe unThermostaty unint temperatureen su constructor, y llama asetTemperature()con ese valor cuando se ejecuta.Main.java: Crea una claseRemoteControl(el invocador) que tenga un métodosetCommand(Command cmd)para almacenar un comando ypressButton()para ejecutar el comando almacenado.En tu método principal, recibirás dos entradas: un número de canal de TV (entero) y una temperatura de termostato (entero).
Crea una
Televisiony unThermostat. Crea unRemoteControly demuestra el Patrón Command mediante:- Configurar un
TVOnCommandy presionar el botón - Configurar un
TVChannelCommandcon tu canal de entrada y presionar el botón - Configurar un
ThermostatSetCommandcon tu temperatura de entrada y presionar el botón - Configurar un
TVOffCommandy presionar el botón
- Configurar un
Recibirás dos entradas en orden: el canal de TV (entero) y la temperatura del termostato (entero).
Por ejemplo, con las entradas 5 y 72, tu salida sería:
TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFF¡Observa cómo el RemoteControl nunca interactúa directamente con los dispositivos; solo conoce la interfaz Command! Cada comando encapsula todo lo necesario para realizar su acción, lo que facilita agregar nuevos dispositivos y comandos sin cambiar el invocador.
Hoja de referencia
El Patrón Command es un patrón de diseño de comportamiento que encapsula una solicitud como un objeto, lo que permite parametrizar clientes con diferentes solicitudes, encolar o registrar solicitudes y admitir operaciones que se pueden deshacer.
El patrón desacopla el objeto que invoca la operación (invocador) del que la realiza (receptor).
Componentes Clave
- Command: Interfaz que declara el método
execute() - ConcreteCommand: Clases que implementan acciones específicas
- Receiver: Objeto que realiza el trabajo real
- Invoker: Objeto que activa los comandos
Estructura Básica
// Interfaz Command
interface Command {
void execute();
}
// Receptor
class Light {
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}
// ConcreteCommand
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
// Invocador
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}Uso
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // Salida: Light is ONEl invocador no sabe qué acción se realizará; simplemente llama a execute(). Esto hace que el sistema sea flexible y extensible, ideal para encolar operaciones, implementar la funcionalidad de deshacer/rehacer o programar tareas.
Pruébalo tú mismo
import java.util.Scanner;
// TODO: Crear la clase RemoteControl (el invocador) con:
// - método setCommand(Command cmd) para almacenar un comando
// - método pressButton() para ejecutar el comando almacenado
class RemoteControl {
// TODO: Implementar RemoteControl
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leer las entradas
int channel = scanner.nextInt();
int temperature = scanner.nextInt();
// TODO: Crear una Television y un Thermostat
// TODO: Crear un RemoteControl
// TODO: Demostrar el Command Pattern:
// 1. Configurar un TVOnCommand y presionar el botón
// 2. Configurar un TVChannelCommand con el canal de entrada y presionar el botón
// 3. Configurar un ThermostatSetCommand con la temperatura de entrada y presionar el botón
// 4. Configurar un TVOffCommand y presionar el botón
scanner.close();
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator