Introdução ao Padrão Command
O padrão Command é um padrão de projeto comportamental que transforma uma solicitação em um objeto independente contendo todas as informações sobre a ação. Essa transformação permite parametrizar métodos com diferentes solicitações, atrasar ou colocar a execução de uma requisição em fila, e suportar operações desfazíveis.
O principal objetivo é desacoplar o objeto que invoca a operação daquele que sabe como realizá-la, permitindo que a comunicação ocorra exclusivamente através do objeto de comando.
Componentes Principais
- Invocador (Invoker): O objeto responsável por iniciar a requisição. Ele mantém uma referência ao objeto de comando e aciona sua execução.
- Comando (Command): Uma interface que declara o método para a execução da ação.
- Comando Concreto (Concrete Command): Implementa a interface de comando e define a ligação entre o objeto receptor e a ação a ser executada.
- Receptor (Receiver): Contém a lógica de negócio real. É o objeto que executa a ação quando o comando é acionado.
- Cliente (Client): Cria e configura os objetos de comando concretos, associando-os aos seus respectivos receptores e invocadores.
Cenários de Aplicação
- Desacoplamento de Camadas: Separar a interface do usuário (como botões ou menus) da lógica de negócio subjacente.
- Histórico de Operações (Undo/Redo): Implementar funcionalidades de desfazer e refazer em editores de texto ou softwarse de design.
- Agendamento e Filas: Armzaenar comandos em uma fila para execução assíncrona ou agendada.
- Gerenciamento de Transações: Agrupar múltiplas operações em uma transação atômica, permitindo rollback em caso de falha.
- Macros: Executar uma sequência de comandos como uma única operação combinada.
Vantagens
- Princípio da Responsabilidade Única: Isola a lógica de execução em classes separadas, facilitando a manutenção.
- Princípio Aberto/Fechado: Novos comandos podem ser introduzidos sem a necessidade de alterar o código existente.
- Controle de Execução: Permite implementar operações de desfazer, refazer e agendamento de forma natural e estruturada.
Desvantagens
- Explosão de Classes: A criação de um novo comando para cada ação pode resultar em um grande número de classes e interfaces no projeto.
- Complexidade Adicional: Para operações triviais e simples, o padrão pode introduzir uma sobrecarga de design desnecessária.
Exemplo de Implementação
Abaixo, apresentamos uma implementação utilizando um cenário de controle de um Drone, onde um painel de controle (Invocador) aciona ações de decolagem e pouso (Comandos) que são efetivamente executadas pelo próprio Drone (Receptor).
Go
package command
import "fmt"
// Action define a interface para o comando
type Action interface {
Run()
}
// FlightController atua como o invocador
type FlightController struct {
action Action
}
func (fc *FlightController) Trigger() {
fc.action.Run()
}
// TakeOffAction é um comando concreto
type TakeOffAction struct {
machine Drone
}
func (t *TakeOffAction) Run() {
t.machine.Ascend()
}
// LandAction é outro comando concreto
type LandAction struct {
machine Drone
}
func (l *LandAction) Run() {
l.machine.Descend()
}
// Drone é o receptor
type Drone struct {
isFlying bool
}
func (d *Drone) Ascend() {
d.isFlying = true
fmt.Println("Drone decolando...")
}
func (d *Drone) Descend() {
d.isFlying = false
fmt.Println("Drone pousando...")
}
Teste do Cliente:
package command
import "testing"
func TestDroneCommand(t *testing.T) {
myDrone := &Drone{}
takeOff := &TakeOffAction{
machine: myDrone,
}
land := &LandAction{
machine: myDrone,
}
controller := &FlightController{
action: takeOff,
}
controller.Trigger()
controller.action = land
controller.Trigger()
}
Python
from abc import ABC, abstractmethod
class Action(ABC):
@abstractmethod
def run(self):
pass
class Machine(ABC):
@abstractmethod
def ascend(self):
pass
@abstractmethod
def descend(self):
pass
class FlightController:
def __init__(self, action: Action):
self.action = action
def trigger(self):
self.action.run()
class TakeOffAction(Action):
def __init__(self, machine: Machine):
self.machine = machine
def run(self):
self.machine.ascend()
class LandAction(Action):
def __init__(self, machine: Machine):
self.machine = machine
def run(self):
self.machine.descend()
class Drone(Machine):
def __init__(self):
self.is_flying = False
def ascend(self):
self.is_flying = True
print("Drone decolando...")
def descend(self):
self.is_flying = False
print("Drone pousando...")
if __name__ == "__main__":
drone = Drone()
take_off_cmd = TakeOffAction(drone)
land_cmd = LandAction(drone)
panel = FlightController(take_off_cmd)
panel.trigger()
panel.action = land_cmd
panel.trigger()