Patrón Command
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 56 de 70.
El patrón Command es un patrón 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, ponerlas en cola, registrarlas o admitir operaciones que se pueden deshacer.
El patrón separa el objeto que invoca una operación del objeto que sabe cómo realizarla. Consta de cuatro componentes clave: una interfaz de comando, comandos concretos, un receptor (el objeto que realiza el trabajo real) y un invocador (el objeto que activa los comandos).
public interface ICommand
{
void Execute();
}
public class Light // Receptor
{
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}
public class TurnOnCommand : ICommand
{
private Light _light;
public TurnOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
}
public class TurnOffCommand : ICommand
{
private Light _light;
public TurnOffCommand(Light light) => _light = light;
public void Execute() => _light.TurnOff();
}El invocador almacena y ejecuta comandos sin saber qué hacen:
public class RemoteControl // Invocador
{
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command.Execute();
}
// Uso
var light = new Light();
var remote = new RemoteControl();
remote.SetCommand(new TurnOnCommand(light));
remote.PressButton(); // La luz está ENCENDIDA
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // La luz está APAGADAEl patrón Command es particularmente potente para implementar la funcionalidad de deshacer, la grabación de macros o el encolado de operaciones 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ácilVamos a construir un sistema de comandos para un editor de texto utilizando el patrón Command. Crearás un sistema donde las operaciones de edición, como insertar y eliminar texto, se encapsulan como objetos de comando, permitiendo que el editor los ejecute a través de una interfaz unificada.
Organizarás tu código en tres archivos:
Command.cs: Define una interfazICommanden el espacio de nombresTextEditorcon un único métodoExecute(). Luego, crea dos clases de comando concretas:InsertCommand: recibe unDocumenty un stringtexten su constructor. Cuando se ejecuta, añade el texto al documento e imprimeInserted: {text}DeleteCommand: recibe unDocumenty unint counten su constructor. Cuando se ejecuta, elimina los últimoscountcaracteres del documento e imprimeDeleted: {count} characters
Document.cs: Crea una claseDocumenten el mismo espacio de nombres. Este es tu receptor (receiver): el objeto que realmente realiza las operaciones de texto. Debe tener:- Un campo de cadena privado para almacenar el contenido (inicializado como una cadena vacía)
- Un método
Append(string text)que añade texto al contenido - Un método
RemoveLast(int count)que elimina los últimoscountcaracteres (si count excede la longitud del contenido, borra todo el contenido) - Un método
GetContent()que devuelve el contenido actual
Program.cs: Crea una claseEditorque actúe como el invocador (invoker). Debe tener un métodoSetCommand(ICommand command)y un métodoExecuteCommand()que ejecute el comando actual. En tu código principal, demuestra el patrón command procesando una serie de operaciones de edición.
Recibirás las siguientes entradas:
- Número de comandos a ejecutar
- Para cada comando: el tipo de comando (
insertodelete) seguido del texto a insertar o el número de caracteres a eliminar
Después de ejecutar todos los comandos, imprime el contenido final del documento en una nueva línea con el formato Content: {content}.
Por ejemplo, si las entradas son:
4
insert
Hello
insert
World
delete
3
insert
!La salida debería ser:
Inserted: Hello
Inserted: World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!Observa cómo el Editor (invocador) no sabe nada sobre la manipulación de texto; simplemente ejecuta cualquier comando que se le asigne. Los comandos encapsulan tanto la acción como el receptor, lo que facilita la adición de nuevas operaciones como ReplaceCommand sin cambiar el editor.
Hoja de referencia
El patrón Command es un patrón de comportamiento que encapsula una solicitud como un objeto independiente que contiene toda la información sobre dicha solicitud. Esto permite pasar solicitudes como argumentos de método, encolarlas, registrarlas o admitir operaciones que se pueden deshacer.
El patrón tiene cuatro componentes clave:
- Interfaz Command: define el método Execute().
- Comandos concretos: implementan la interfaz de comando y encapsulan acciones específicas.
- Receptor (Receiver): el objeto que realiza el trabajo real.
- Invocador (Invoker): el objeto que activa los comandos sin conocer su implementación.
Interfaz Command y Comandos Concretos
public interface ICommand
{
void Execute();
}
public class TurnOnCommand : ICommand
{
private Light _light;
public TurnOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
}
public class TurnOffCommand : ICommand
{
private Light _light;
public TurnOffCommand(Light light) => _light = light;
public void Execute() => _light.TurnOff();
}Receptor (Receiver)
public class Light
{
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}Invocador (Invoker)
public class RemoteControl
{
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command.Execute();
}Uso
var light = new Light();
var remote = new RemoteControl();
remote.SetCommand(new TurnOnCommand(light));
remote.PressButton(); // Light is ON
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // Light is OFFEl patrón Command es útil para implementar la funcionalidad de deshacer, la grabación de macros o la puesta en cola de operaciones para su ejecución posterior. Cada comando encapsula todo lo necesario para realizar una acción, lo que hace que los sistemas sean más flexibles y extensibles.
Pruébalo tú mismo
using System;
using TextEditor;
// TODO: Crea la clase Editor (el invoker)
public class Editor
{
// TODO: Añade un campo privado para almacenar el comando actual
private ICommand _command;
// TODO: Implementa el método SetCommand
public void SetCommand(ICommand command)
{
// TODO: Almacena el comando
}
// TODO: Implementa el método ExecuteCommand
public void ExecuteCommand()
{
// TODO: Ejecuta el comando actual
}
}
class Program
{
public static void Main(string[] args)
{
// Lee el número de comandos
int n = Convert.ToInt32(Console.ReadLine());
// Crea el documento (receiver)
Document document = new Document();
// Crea el editor (invoker)
Editor editor = new Editor();
// TODO: Procesa cada comando
for (int i = 0; i < n; i++)
{
string commandType = Console.ReadLine();
// TODO: Basado en commandType, crea el comando apropiado
// - Si es "insert": lee el texto y crea InsertCommand
// - Si es "delete": lee la cantidad y crea DeleteCommand
// Luego establece el comando en el editor y ejecútalo
}
// TODO: Imprime el contenido final con el formato "Content: {content}"
}
}
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 externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes