Padrão de Comando
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 91 de 107.
O padrão Command encapsula uma solicitação como um objeto, permitindo parametrizar operações, colocá-las em uma fila ou oferecer suporte à funcionalidade de desfazer. Enquanto o Strategy encapsula algoritmos, o Command encapsula ações completas junto com seus parâmetros.
Em Go, definimos uma interface Command com um método Execute e, em seguida, criamos comandos concretos que contêm todas as informações necessárias para executar uma ação:
type Command interface {
Execute() string
}
type Light struct {
IsOn bool
}
type TurnOnCommand struct {
light *Light
}
func (c *TurnOnCommand) Execute() string {
c.light.IsOn = true
return "Light turned on"
}
type TurnOffCommand struct {
light *Light
}
func (c *TurnOffCommand) Execute() string {
c.light.IsOn = false
return "Light turned off"
}Um invocador armazena e executa comandos sem saber o que eles fazem:
type RemoteControl struct {
command Command
}
func (r *RemoteControl) SetCommand(c Command) {
r.command = c
}
func (r *RemoteControl) PressButton() string {
return r.command.Execute()
}O invocador é completamente desacoplado do receptor (a luz):
light := &Light{}
remote := &RemoteControl{}
remote.SetCommand(&TurnOnCommand{light: light})
fmt.Println(remote.PressButton()) // Luz ligada
remote.SetCommand(&TurnOffCommand{light: light})
fmt.Println(remote.PressButton()) // Luz desligadaCommand é ideal para implementar sistemas de desfazer/refazer, filas de tarefas ou gravação de macros quando você precisa armazenar, adiar ou reproduzir operações.
Desafio
FácilVamos criar um editor de texto com funcionalidade de desfazer usando o padrão Command! Você criará comandos que encapsulam operações de texto, permitindo que o editor execute ações e as reverta: um caso de uso clássico que demonstra por que os comandos são tão poderosos.
Você organizará seu código em três arquivos:
command.go: Defina sua interface de comando e os comandos concretos de edição de texto.Crie uma interface
Commandcom dois métodos:Execute() stringpara realizar a ação eUndo() stringpara revertê-la.Implemente dois tipos de comando que operam em um
*TextEditor:InsertCommand: contém o ponteiro do editor e o texto a inserir. Execute acrescenta o texto ao conteúdo do editor e retornaInserted: [text]. Undo remove esse texto do final e retornaUndone insert: [text]DeleteCommand: contém o ponteiro do editor e uma quantidade de caracteres a excluir do final. Execute remove esses caracteres (armazenando-os para desfazer) e retornaDeleted: [removed text]. Undo os restaura e retornaUndone delete: [restored text]
editor.go: Crie seu editor de texto (o receptor) e o invocador que gerencia o histórico de comandos.Crie uma struct
TextEditorcom um campoContent(string) e métodos paraAppend(text string)eDeleteLast(count int) string(retorna o texto excluído).Crie uma struct
EditorInvokerque contenha um slice de comandos executados como histórico. Adicione estes métodos:ExecuteCommand(cmd Command) string: executa o comando, adiciona-o ao histórico e retorna o resultadoUndoLast() string: remove o último comando do histórico, chama seu Undo e retorna o resultado. Se o histórico estiver vazio, retornaNothing to undo
main.go: Demonstre seu sistema de comandos com uma série de operações.Leia uma quantidade de operações. Para cada operação, leia o tipo de operação (
insert,deleteouundo). Para insert, leia também o texto a inserir. Para delete, leia a quantidade de caracteres. Execute cada operação por meio do seu invocador e imprima o resultado. Depois de todas as operações, imprima o conteúdo final do editor comoFinal: [content].
As seguintes entradas serão fornecidas:
- Linha 1: Número de operações
- Para cada operação: tipo de operação, depois os dados adicionais, se necessários (texto para insert, quantidade para delete)
Por exemplo, dada a entrada:
5
insert
Hello
insert
World
delete
3
undo
undoSua saída deve ser:
Inserted: Hello
Inserted: World
Deleted: rld
Undone delete: rld
Undone insert: World
Final: HelloE dada a entrada:
4
insert
Go
insert
Lang
undo
insert
!Sua saída deve ser:
Inserted: Go
Inserted: Lang
Undone insert: Lang
Inserted: !
Final: Go!E dada a entrada:
2
undo
insert
TestSua saída deve ser:
Nothing to undo
Inserted: Test
Final: TestObserve como cada comando encapsula tudo o que é necessário tanto para realizar quanto para reverter sua ação. O invocador não sabe o que os comandos fazem. Ele apenas os executa e mantém o histórico para oferecer suporte à funcionalidade de desfazer!
REQUIRED OUTPUT FORMAT:Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Ler o número de operações
line, _ := reader.ReadString('\n')
numOps, _ := strconv.Atoi(strings.TrimSpace(line))
// Criar o editor e o invoker
editor := &TextEditor{}
invoker := &EditorInvoker{}
for i := 0; i < numOps; i++ {
// Ler o tipo de operação
opLine, _ := reader.ReadString('\n')
opType := strings.TrimSpace(opLine)
var result string
switch opType {
case "insert":
// Ler o texto a inserir
textLine, _ := reader.ReadString('\n')
text := strings.TrimSuffix(textLine, "\n")
// TODO: Criar um InsertCommand e executá-lo através do invoker
_ = text
_ = editor
case "delete":
// Ler a quantidade de caracteres a deletar
countLine, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countLine))
// TODO: Criar um DeleteCommand e executá-lo através do invoker
_ = count
case "undo":
// TODO: Chamar UndoLast no invoker
}
fmt.Println(result)
}
// Imprimir o conteúdo final
fmt.Printf("Final: %s\n", editor.Content)
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos11Biblioteca Padrão e POO
io.Reader e io.Writersort.InterfaceInterface fmt.Stringerencoding/json com StructsInterface http.HandlerRevisão - Modelos de API REST14Padrões de Projeto — Parte 2
Padrão de ComandoPadrão AdapterPadrão DecoratorPadrão de Método TemplatePadrão de EstadoPadrão CompositeMiddleware como Decorator3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online