Menu
CoddyTech

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, enfileirá-las ou oferecer suporte à funcionalidade de desfazer. Enquanto Strategy encapsula algoritmos, Command encapsula ações inteiras 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 realizar 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 está completamente desacoplado do receptor (a Light):

light := &Light{}
remote := &RemoteControl{}

remote.SetCommand(&TurnOnCommand{light: light})
fmt.Println(remote.PressButton())  // Luz acesa

remote.SetCommand(&TurnOffCommand{light: light})
fmt.Println(remote.PressButton())  // Luz apagada

Command é ideal para implementar sistemas de desfazer/refazer, filas de tarefas ou gravação de macros quando você precisa armazenar, adiar ou reproduzir operações.

challenge icon

Desafio

Fácil

Vamos 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 Command com dois métodos: Execute() string para realizar a ação e Undo() string para revertê-la.

    Implemente dois tipos de comando que operam sobre um *TextEditor:

    • InsertCommand: armazena o ponteiro do editor e o texto a ser inserido. Execute adiciona o texto ao conteúdo do editor e retorna Inserted: [text]. Undo remove esse texto do final e retorna Undone insert: [text]
    • DeleteCommand: armazena o ponteiro do editor e a quantidade de caracteres a excluir do final. Execute remove esses caracteres (armazenando-os para desfazer a ação) e retorna Deleted: [removed text]. Undo os restaura e retorna Undone 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 TextEditor com um campo Content (string) e os métodos Append(text string) e DeleteLast(count int) string (retorna o texto excluído).

    Crie uma struct EditorInvoker que armazena uma lista 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 resultado
    • UndoLast() string: remove o último comando do histórico, chama seu Undo e retorna o resultado. Se o histórico estiver vazio, retorna Nothing to undo
  • main.go: Demonstre seu sistema de comandos com uma série de operações.

    Leia a quantidade de operações. Para cada operação, leia o tipo da operação (insert, delete ou undo). Para inserir, leia também o texto a inserir. Para excluir, leia a quantidade de caracteres. Execute cada operação por meio do invocador e imprima o resultado. Após todas as operações, imprima o conteúdo final do editor como Final: [content].

As seguintes entradas serão fornecidas:

  • Linha 1: Número de operações
  • Para cada operação: tipo da operação e, em seguida, dados adicionais, se necessário (texto para insert, quantidade para delete)

Por exemplo, dada a entrada:

5
insert
Hello
insert
 World
delete
3
undo
undo

Sua saída deve ser:

Inserted: Hello
Inserted:  World
Deleted: rld
Undone delete: rld
Undone insert:  World
Final: Hello

E 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
Test

Sua saída deve ser:

Nothing to undo
Inserted: Test
Final: Test

Observe 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 permitir desfazer ações!

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leia o número de operações
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// Crie o editor e o invocador
	editor := &TextEditor{}
	invoker := &EditorInvoker{}
	
	for i := 0; i < numOps; i++ {
		// Leia o tipo de operação
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		var result string
		
		switch opType {
		case "insert":
			// Leia o texto a ser inserido
			textLine, _ := reader.ReadString('\n')
			text := strings.TrimSuffix(textLine, "\n")
			
			// TODO: Crie um InsertCommand e execute-o por meio do invocador
			_ = text
			_ = editor
			
		case "delete":
			// Leia a quantidade de caracteres a serem excluídos
			countLine, _ := reader.ReadString('\n')
			count, _ := strconv.Atoi(strings.TrimSpace(countLine))
			
			// TODO: Crie um DeleteCommand e execute-o por meio do invocador
			_ = count
			
		case "undo":
			// TODO: Chame UndoLast no invocador
			
		}
		
		fmt.Println(result)
	}
	
	// Imprima o conteúdo final
	fmt.Printf("Final: %s\n", editor.Content)
}
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de Go online