Patrón de comportamiento Command(Python) - fapaccha/Dise-o-dirigido-por-el-modelo GitHub Wiki
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 te permite parametrizar los métodos con diferentes solicitudes, retrasar o poner en cola la ejecución de una solicitud y soportar operaciones que no se pueden realizar.
Estructura
- La clase Emisora (o invocadora) es responsable de inicializar las solicitudes. Esta clase debe tener un campo para almacenar una referencia a un objeto de comando. El emisor activa este comando en lugar de enviar la solicitud directamente al receptor. Ten en cuenta que el emisor no es responsable de crear el objeto de comando. Normalmente, obtiene un comando precreado de parte del cliente a través del constructor.
- La interfaz Comando normalmente declara un único método para ejecutar el comando.
- os Comandos Concretos implementan varios tipos de solicitudes. Un comando concreto no se supone que tenga que realizar el trabajo por su cuenta, sino pasar la llamada a uno de los objetos de la lógica de negocio. Sin embargo, para lograr simplificar el código, estas clases se pueden fusionar.
Los parámetros necesarios para ejecutar un método en un objeto receptor pueden declararse como campos en el comando concreto. Puedes hacer inmutables los objetos de comando permitiendo la inicialización de estos campos únicamente a través del constructor. 4. La clase Receptora contiene cierta lógica de negocio. Casi cualquier objeto puede actuar como receptor. La mayoría de los comandos solo gestiona los detalles sobre cómo se pasa una solicitud al receptor, mientras que el propio receptor hace el trabajo real. 5. El Cliente crea y configura los objetos de comando concretos. El cliente debe pasar todos los parámetros de la solicitud, incluyendo una instancia del receptor, dentro del constructor del comando. Después de eso, el comando resultante puede asociarse con uno o varios emisores.
Cómo implementarlo
-
Declara la interfaz de comando con un único método de ejecución.
-
Empieza extrayendo solicitudes y poniéndolas dentro de clases concretas de comando que implementen la interfaz de comando. Cada clase debe contar con un grupo de campos para almacenar los argumentos de las solicitudes junto con referencias al objeto receptor. Todos estos valores deben inicializarse a través del constructor del comando.
-
Identifica clases que actúen como emisoras. Añade los campos para almacenar comandos dentro de estas clases. Las emisoras deberán comunicarse con sus comandos tan solo a través de la interfaz de comando. Normalmente las emisoras no crean objetos de comando por su cuenta, sino que los obtienen del código cliente.
-
Cambia las emisoras de forma que ejecuten el comando en lugar de enviar directamente una solicitud al receptor.
-
El cliente debe inicializar objetos en el siguiente orden:
- Crear receptores.
- Crear comandos y asociarlos con receptores si es necesario.
- Crear emisores y asociarlos con comandos específicos.
Ejemplo:
// La clase base comando define la interfaz común a todos los
// comandos concretos.
abstract class Command is
protected field app: Application
protected field editor: Editor
protected field backup: text
constructor Command(app: Application, editor: Editor) is
this.app = app
this.editor = editor
// Realiza una copia de seguridad del estado del editor.
method saveBackup() is
backup = editor.text
// Restaura el estado del editor.
method undo() is
editor.text = backup
// El método de ejecución se declara abstracto para forzar a
// todos los comandos concretos a proporcionar sus propias
// implementaciones. El método debe devolver verdadero o
// falso dependiendo de si el comando cambia el estado del
// editor.
abstract method execute()
// Los comandos concretos van aquí.
class CopyCommand extends Command is
// El comando copiar no se guarda en el historial ya que no
// cambia el estado del editor.
method execute() is
app.clipboard = editor.getSelection()
return false
class CutCommand extends Command is
// El comando cortar no cambia el estado del editor, por lo
// que debe guardarse en el historial. Y se guardará siempre
// y cuando el método devuelva verdadero.
method execute() is
saveBackup()
app.clipboard = editor.getSelection()
editor.deleteSelection()
return true
class PasteCommand extends Command is
method execute() is
saveBackup()
editor.replaceSelection(app.clipboard)
return true
// La operación deshacer también es un comando.
class UndoCommand extends Command is
method execute() is
app.undo()
return false
// El historial global de comandos tan solo es una pila.
class CommandHistory is
private field history: array of Command
// El último dentro...
method push(c: Command) is
// Empuja el comando al final de la matriz del
// historial.
// ...el primero fuera.
method pop():Command is
// Obtiene el comando más reciente del historial.
// La clase editora tiene operaciones reales de edición de
// texto. Juega el papel de un receptor: todos los comandos
// acaban delegando la ejecución a los métodos del editor.
class Editor is
field text: string
method getSelection() is
// Devuelve el texto seleccionado.
method deleteSelection() is
// Borra el texto seleccionado.
method replaceSelection(text) is
// Inserta los contenidos del portapapeles en la
// posición actual.
// La clase Aplicación establece relaciones entre objetos. Actúa
// como un emisor: cuando algo debe hacerse, crea un objeto de
// comando y lo ejecuta.
class Application is
field clipboard: string
field editors: array of Editors
field activeEditor: Editor
field history: CommandHistory
// El código que asigna comandos a objetos UI puede tener
// este aspecto.
method createUI() is
// ...
copy = function() { executeCommand(
new CopyCommand(this, activeEditor)) }
copyButton.setCommand(copy)
shortcuts.onKeyPress("Ctrl+C", copy)
cut = function() { executeCommand(
new CutCommand(this, activeEditor)) }
cutButton.setCommand(cut)
shortcuts.onKeyPress("Ctrl+X", cut)
paste = function() { executeCommand(
new PasteCommand(this, activeEditor)) }
pasteButton.setCommand(paste)
shortcuts.onKeyPress("Ctrl+V", paste)
undo = function() { executeCommand(
new UndoCommand(this, activeEditor)) }
undoButton.setCommand(undo)
shortcuts.onKeyPress("Ctrl+Z", undo)
// Ejecuta un comando y comprueba si debe añadirse al
// historial.
method executeCommand(command) is
if (command.execute)
history.push(command)
// Toma el comando más reciente del historial y ejecuta su
// método deshacer. Observa que no conocemos la clase de ese
// comando. Pero no tenemos por qué, ya que el comando sabe
// cómo deshacer su propia acción.
method undo() is
command = history.pop()
if (command != null)
command.undo()