Skip to content

Latest commit

 

History

History
171 lines (132 loc) · 5.73 KB

File metadata and controls

171 lines (132 loc) · 5.73 KB

Command

O que é?

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 usar?

  • 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

Exemplo em Python

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()

Vantagens

  1. Desacopla classes que invocam operações das classes que executam estas operações
  2. Permite implementar desfazer/refazer
  3. Permite construir operações complexas a partir de operações simples
  4. Implementa o princípio de Responsabilidade Única
  5. Implementa o princípio Aberto/Fechado

Desvantagens

  1. Pode levar a um grande número de classes comando
  2. Aumenta a complexidade do código
  3. Alguns comandos podem ser simples demais para justificar uma classe separada

Considerações de Implementação

  • 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