Menu
Coddy logo textTech

Wzorzec polecenia

Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 91 z 107.

Wzorzec polecenia enkapsuluje żądanie jako obiekt, umożliwiając parametryzowanie operacji, umieszczanie ich w kolejce lub obsługę funkcji cofania. Podczas gdy wzorzec Strategy enkapsuluje algorytmy, wzorzec Command enkapsuluje całe działania wraz z ich parametrami.

W Go definiujemy interfejs Command z metodą Execute, a następnie tworzymy konkretne polecenia, które przechowują wszystkie informacje potrzebne do wykonania działania:

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

Wywołujący przechowuje i wykonuje polecenia, nie wiedząc, co one robią:

type RemoteControl struct {
    command Command
}

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

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

Wywołujący jest całkowicie oddzielony od odbiorcy (światła):

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

remote.SetCommand(&TurnOnCommand{light: light})
fmt.Println(remote.PressButton())  // Światło włączone

remote.SetCommand(&TurnOffCommand{light: light})
fmt.Println(remote.PressButton())  // Światło wyłączone

Wzorzec Command idealnie nadaje się do implementowania systemów cofania i ponawiania, kolejek zadań lub nagrywania makr, gdy trzeba przechowywać, opóźniać lub odtwarzać operacje.

challenge icon

Wyzwanie

Łatwy

Zbudujmy edytor tekstu z funkcją cofania, używając wzorca polecenia! Utworzysz polecenia, które kapsułkują operacje na tekście, umożliwiając edytorowi wykonywanie działań i ich odwracanie: to klasyczny przypadek użycia, który pokazuje, dlaczego polecenia są tak potężne.

Podzielisz swój kod na trzy pliki:

  • command.go: Zdefiniuj interfejs polecenia i konkretne polecenia do edycji tekstu.

    Utwórz interfejs Command z dwiema metodami: Execute() string, która wykonuje działanie, oraz Undo() string, która je odwraca.

    Zaimplementuj dwa typy poleceń działające na *TextEditor:

    • InsertCommand: przechowuje wskaźnik do edytora i tekst do wstawienia. Execute dołącza tekst do zawartości edytora i zwraca Inserted: [text]. Undo usuwa ten tekst z końca i zwraca Undone insert: [text]
    • DeleteCommand: przechowuje wskaźnik do edytora i liczbę znaków do usunięcia z końca. Execute usuwa te znaki (zapisując je na potrzeby cofania) i zwraca Deleted: [removed text]. Undo przywraca je i zwraca Undone delete: [restored text]
  • editor.go: Utwórz edytor tekstu (odbiorcę) i obiekt wywołujący, który zarządza historią poleceń.

    Zbuduj strukturę TextEditor z polem Content (string) oraz metodami Append(text string) i DeleteLast(count int) string (zwraca usunięty tekst).

    Zbuduj strukturę EditorInvoker, która przechowuje listę wykonanych poleceń jako historię. Dodaj następujące metody:

    • ExecuteCommand(cmd Command) string: wykonuje polecenie, dodaje je do historii i zwraca wynik
    • UndoLast() string: usuwa ostatnie polecenie z historii, wywołuje jego Undo i zwraca wynik. Jeśli historia jest pusta, zwróć Nothing to undo
  • main.go: Zademonstruj działanie systemu poleceń za pomocą serii operacji.

    Wczytaj liczbę operacji. Dla każdej operacji wczytaj jej typ (insert, delete lub undo). W przypadku insert wczytaj również tekst do wstawienia. W przypadku delete wczytaj liczbę znaków. Wykonaj każdą operację za pomocą obiektu wywołującego i wypisz wynik. Po wykonaniu wszystkich operacji wypisz końcową zawartość edytora jako Final: [content].

Dane wejściowe będą miały następującą postać:

  • Wiersz 1: Liczba operacji
  • Dla każdej operacji: typ operacji, a następnie dodatkowe dane, jeśli są potrzebne (tekst dla insert, liczba dla delete)

Na przykład dla danych wejściowych:

5
insert
Hello
insert
 World
delete
3
undo
undo

Oczekiwany wynik:

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

A dla danych wejściowych:

4
insert
Go
insert
Lang
undo
insert
!

Oczekiwany wynik:

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

A dla danych wejściowych:

2
undo
insert
Test

Oczekiwany wynik:

Nothing to undo
Inserted: Test
Final: Test

Zauważ, że każde polecenie kapsułkuje wszystko, co jest potrzebne do wykonania i odwrócenia jego działania. Obiekt wywołujący nie wie, co robią polecenia. Po prostu je wykonuje i prowadzi historię, aby umożliwić cofanie!

Spróbuj swoich sił

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Odczytaj liczbę operacji
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// Utwórz edytor i obiekt wywołujący
	editor := &TextEditor{}
	invoker := &EditorInvoker{}
	
	for i := 0; i < numOps; i++ {
		// Odczytaj typ operacji
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		var result string
		
		switch opType {
		case "insert":
			// Odczytaj tekst do wstawienia
			textLine, _ := reader.ReadString('\n')
			text := strings.TrimSuffix(textLine, "\n")
			
			// TODO: Utwórz InsertCommand i wykonaj go za pomocą obiektu wywołującego
			_ = text
			_ = editor
			
		case "delete":
			// Odczytaj liczbę znaków do usunięcia
			countLine, _ := reader.ReadString('\n')
			count, _ := strconv.Atoi(strings.TrimSpace(countLine))
			
			// TODO: Utwórz DeleteCommand i wykonaj go za pomocą obiektu wywołującego
			_ = count
			
		case "undo":
			// TODO: Wywołaj UndoLast na obiekcie wywołującym
			
		}
		
		fmt.Println(result)
	}
	
	// Wyświetl końcową zawartość
	fmt.Printf("Final: %s\n", editor.Content)
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Go online