Menu
Coddy logo textTech

Pattern Command

Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 91 di 107.

Il pattern Command incapsula una richiesta come oggetto, consentendoti di parametrizzare le operazioni, metterle in coda o supportare la funzionalità di annullamento. Mentre Strategy incapsula algoritmi, Command incapsula azioni complete insieme ai relativi parametri.

In Go, definiamo un'interfaccia Command con un metodo Execute, quindi creiamo comandi concreti che contengono tutte le informazioni necessarie per eseguire un'azione:

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

Un invocatore memorizza ed esegue i comandi senza sapere cosa fanno:

type RemoteControl struct {
    command Command
}

func (r *RemoteControl) SetCommand(c Command) {
    r.command = c
}

func (r *RemoteControl) PressButton() string {
    return r.command.Execute()
}

L'invocatore è completamente disaccoppiato dal destinatario (la luce):

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

remote.SetCommand(&TurnOnCommand{light: light})
fmt.Println(remote.PressButton())  // Luce accesa

remote.SetCommand(&TurnOffCommand{light: light})
fmt.Println(remote.PressButton())  // Luce spenta

Command è ideale per implementare sistemi di annullamento/ripristino, code di attività o registrazione di macro quando hai bisogno di memorizzare, ritardare o riprodurre operazioni.

challenge icon

Sfida

Facile

Costruiamo un editor di testo con funzionalità di annullamento usando il pattern Command! Creerai comandi che incapsulano operazioni sul testo, consentendo all'editor di eseguire azioni e annullarle: un classico caso d'uso che dimostra perché i comandi sono così potenti.

Organizzerai il codice in tre file:

  • command.go: definisci la tua interfaccia dei comandi e i comandi concreti per modificare il testo.

    Crea un'interfaccia Command con due metodi: Execute() string per eseguire l'azione e Undo() string per annullarla.

    Implementa due tipi di comando che operano su un *TextEditor:

    • InsertCommand: contiene il puntatore all'editor e il testo da inserire. Execute aggiunge il testo al contenuto dell'editor e restituisce Inserted: [text]. Undo rimuove quel testo dalla fine e restituisce Undone insert: [text]
    • DeleteCommand: contiene il puntatore all'editor e il numero di caratteri da eliminare dalla fine. Execute rimuove quei caratteri (memorizzandoli per l'annullamento) e restituisce Deleted: [removed text]. Undo li ripristina e restituisce Undone delete: [restored text]
  • editor.go: crea il tuo editor di testo (il receiver) e l'invoker che gestisce la cronologia dei comandi.

    Crea una struct TextEditor con un campo Content (string) e i metodi Append(text string) e DeleteLast(count int) string (restituisce il testo eliminato).

    Crea una struct EditorInvoker che contiene una slice dei comandi eseguiti come cronologia. Aggiungi questi metodi:

    • ExecuteCommand(cmd Command) string: esegue il comando, lo aggiunge alla cronologia e restituisce il risultato
    • UndoLast() string: rimuove l'ultimo comando dalla cronologia, chiama il suo Undo e restituisce il risultato. Se la cronologia è vuota, restituisci Nothing to undo
  • main.go: mostra il tuo sistema di comandi con una serie di operazioni.

    Leggi il numero di operazioni. Per ciascuna operazione, leggi il tipo (insert, delete o undo). Per insert, leggi anche il testo da inserire. Per delete, leggi il numero di caratteri. Esegui ogni operazione tramite il tuo invoker e stampa il risultato. Dopo tutte le operazioni, stampa il contenuto finale dell'editor come Final: [content].

Verranno forniti i seguenti input:

  • Riga 1: numero di operazioni
  • Per ogni operazione: tipo di operazione, poi eventuali dati aggiuntivi (testo per insert, numero per delete)

Ad esempio, dato:

5
insert
Hello
insert
 World
delete
3
undo
undo

Il tuo output dovrebbe essere:

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

E dato:

4
insert
Go
insert
Lang
undo
insert
!

Il tuo output dovrebbe essere:

Inserted: Go
Inserted: Lang
Undone insert: Lang
Inserted: !
Final: Go!

E dato:

2
undo
insert
Test

Il tuo output dovrebbe essere:

Nothing to undo
Inserted: Test
Final: Test

Nota come ogni comando incapsula tutto ciò che serve sia per eseguire sia per annullare la propria azione. L'invoker non sa cosa fanno i comandi. Li esegue soltanto e mantiene la cronologia per supportare l'annullamento!

Provalo tu

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leggi il numero di operazioni
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// Crea l'editor e l'invocatore
	editor := &TextEditor{}
	invoker := &EditorInvoker{}
	
	for i := 0; i < numOps; i++ {
		// Leggi il tipo di operazione
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		var result string
		
		switch opType {
		case "insert":
			// Leggi il testo da inserire
			textLine, _ := reader.ReadString('\n')
			text := strings.TrimSuffix(textLine, "\n")
			
			// TODO: Crea un InsertCommand ed eseguilo tramite l'invocatore
			_ = text
			_ = editor
			
		case "delete":
			// Leggi il numero di caratteri da eliminare
			countLine, _ := reader.ReadString('\n')
			count, _ := strconv.Atoi(strings.TrimSpace(countLine))
			
			// TODO: Crea un DeleteCommand ed eseguilo tramite l'invocatore
			_ = count
			
		case "undo":
			// TODO: Chiama UndoLast sull'invocatore
			
		}
		
		fmt.Println(result)
	}
	
	// Stampa il contenuto finale
	fmt.Printf("Final: %s\n", editor.Content)
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Go online