Menu
CoddyTech

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, permettendoti 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 invoker 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'invoker è completamente disaccoppiato dal ricevitore (la Light):

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/ripetizione, code di attività o registrazione di macro, quando devi memorizzare, ritardare o ripetere operazioni.

challenge icon

Sfida

Facile

Costruiamo un editor di testo con funzionalità di annullamento usando il pattern Command! Creerai comandi che incapsulano le operazioni sul testo, consentendo all’editor di eseguire azioni e annullarle: un classico caso d’uso che dimostra quanto siano potenti i comandi.

Organizzerai il codice in tre file:

  • command.go: definisci l’interfaccia dei comandi e i comandi concreti per la modifica del 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 (salvandoli per l’annullamento) e restituisce Deleted: [removed text]. Undo li ripristina e restituisce Undone delete: [restored text]
  • editor.go: crea l’editor di testo (il ricevitore) e l’invocatore che gestisce la cronologia dei comandi.

    Crea una struct TextEditor con un campo Content (stringa) e i metodi Append(text string) e DeleteLast(count int) string (che 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, ne chiama Undo e restituisce il risultato. Se la cronologia è vuota, restituisce Nothing to undo
  • main.go: mostra il funzionamento del sistema di comandi con una serie di operazioni.

    Leggi il numero di operazioni. Per ogni operazione, leggi il tipo di operazione (insert, delete o undo). Per insert, leggi anche il testo da inserire. Per delete, leggi il numero di caratteri. Esegui ogni operazione tramite l’invocatore 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)

Per esempio, dato:

5
insert
Hello
insert
 World
delete
3
undo
undo

L’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
!

L’output dovrebbe essere:

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

E dato:

2
undo
insert
Test

L’output dovrebbe essere:

Nothing to undo
Inserted: Test
Final: Test

Nota come ogni comando incapsuli tutto ciò che serve sia per eseguire la propria azione sia per annullarla. L’invocatore non sa cosa fanno i comandi. Si limita a eseguirli e a mantenere 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