Menu
Coddy logo textTech

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 desligada

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 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 retorna Inserted: [text]. Undo remove esse texto do final e retorna Undone 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 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 métodos para Append(text string) e DeleteLast(count int) string (retorna o texto excluído).

    Crie uma struct EditorInvoker que 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 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 uma quantidade de operações. Para cada operação, leia o tipo de operação (insert, delete ou undo). 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 como Final: [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
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 oferecer suporte à funcionalidade de desfazer!

REQUIRED OUTPUT FORMAT: [Your translated content here]

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