O Command é um padrão comportamental que transforma um pedido em um objeto independente que contém toda a informação sobre o pedido. Essa transformação permite parametrizar métodos com diferentes pedidos, atrasar ou enfileirar a execução de um pedido e suportar operações que podem ser desfeitas.
- Quando você precisa parametrizar objetos com operações
- Quando você precisa enfileirar operações, agendar sua execução ou executá-las remotamente
- Quando você precisa implementar operações reversíveis (undo/redo)
- Quando você quer estruturar um sistema em torno de operações de alto nível construídas sobre operações primitivas
from abc import ABC, abstractmethod
from typing import List
# Command Interface
class Command(ABC):
@abstractmethod
def execute(self) -> None:
pass
@abstractmethod
def undo(self) -> None:
pass
# Receiver
class EditorTexto:
def __init__(self):
self.texto = ""
self.selecao_inicio = 0
self.selecao_fim = 0
def get_selecao(self) -> str:
return self.texto[self.selecao_inicio:self.selecao_fim]
def deletar_selecao(self) -> str:
texto_deletado = self.get_selecao()
self.texto = self.texto[:self.selecao_inicio] + self.texto[self.selecao_fim:]
self.selecao_fim = self.selecao_inicio
return texto_deletado
def inserir_texto(self, texto: str) -> None:
self.texto = self.texto[:self.selecao_inicio] + texto + self.texto[self.selecao_fim:]
self.selecao_inicio += len(texto)
self.selecao_fim = self.selecao_inicio
def selecionar(self, inicio: int, fim: int) -> None:
self.selecao_inicio = max(0, min(inicio, len(self.texto)))
self.selecao_fim = max(0, min(fim, len(self.texto)))
# Comandos Concretos
class ComandoInserir(Command):
def __init__(self, editor: EditorTexto, texto: str):
self.editor = editor
self.texto = texto
self.texto_deletado = ""
self.posicao_antiga = 0
def execute(self) -> None:
self.posicao_antiga = self.editor.selecao_inicio
self.texto_deletado = self.editor.get_selecao()
self.editor.inserir_texto(self.texto)
def undo(self) -> None:
self.editor.selecionar(self.posicao_antiga, self.posicao_antiga + len(self.texto))
self.editor.deletar_selecao()
self.editor.inserir_texto(self.texto_deletado)
self.editor.selecionar(self.posicao_antiga, self.posicao_antiga)
class ComandoDeletar(Command):
def __init__(self, editor: EditorTexto):
self.editor = editor
self.texto_deletado = ""
self.posicao_antiga = 0
def execute(self) -> None:
self.posicao_antiga = self.editor.selecao_inicio
self.texto_deletado = self.editor.deletar_selecao()
def undo(self) -> None:
self.editor.selecionar(self.posicao_antiga, self.posicao_antiga)
self.editor.inserir_texto(self.texto_deletado)
self.editor.selecionar(self.posicao_antiga, self.posicao_antiga + len(self.texto_deletado))
# Invoker
class EditorHistorico:
def __init__(self):
self.historico: List[Command] = []
self.atual = -1
def executar(self, comando: Command) -> None:
# Limpa o histórico futuro se estivermos no meio do histórico
self.historico = self.historico[:self.atual + 1]
comando.execute()
self.historico.append(comando)
self.atual += 1
def desfazer(self) -> None:
if self.atual >= 0:
self.historico[self.atual].undo()
self.atual -= 1
def refazer(self) -> None:
if self.atual + 1 < len(self.historico):
self.atual += 1
self.historico[self.atual].execute()
def main():
# Configuração
editor = EditorTexto()
historico = EditorHistorico()
# Testando os comandos
print("Estado inicial:", editor.texto)
# Inserindo texto
cmd1 = ComandoInserir(editor, "Olá ")
historico.executar(cmd1)
print("Após primeira inserção:", editor.texto)
cmd2 = ComandoInserir(editor, "mundo!")
historico.executar(cmd2)
print("Após segunda inserção:", editor.texto)
# Selecionando e deletando texto
editor.selecionar(4, 9) # Seleciona "mundo"
cmd3 = ComandoDeletar(editor)
historico.executar(cmd3)
print("Após deletar:", editor.texto)
# Desfazendo operações
historico.desfazer() # Desfaz a deleção
print("Após desfazer deleção:", editor.texto)
historico.desfazer() # Desfaz segunda inserção
print("Após desfazer segunda inserção:", editor.texto)
# Refazendo operações
historico.refazer() # Refaz segunda inserção
print("Após refazer segunda inserção:", editor.texto)
if __name__ == "__main__":
main()- Desacopla classes que invocam operações das classes que executam estas operações
- Permite implementar desfazer/refazer
- Permite construir operações complexas a partir de operações simples
- Implementa o princípio de Responsabilidade Única
- Implementa o princípio Aberto/Fechado
- Pode levar a um grande número de classes comando
- Aumenta a complexidade do código
- Alguns comandos podem ser simples demais para justificar uma classe separada
- Use o padrão Command quando precisar de callbacks parametrizados
- Implemente o método undo considerando o estado do sistema
- Use composição para criar comandos complexos
- Considere usar o Memento para armazenar estados para operações undo
- Considere usar o Command com outros padrões como Composite para macrocomandos