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łączoneWzorzec Command idealnie nadaje się do implementowania systemów cofania i ponawiania, kolejek zadań lub nagrywania makr, gdy trzeba przechowywać, opóźniać lub odtwarzać operacje.
Wyzwanie
ŁatwyZbudujmy 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
Commandz dwiema metodami:Execute() string, która wykonuje działanie, orazUndo() 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 zwracaInserted: [text]. Undo usuwa ten tekst z końca i zwracaUndone 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 zwracaDeleted: [removed text]. Undo przywraca je i zwracaUndone delete: [restored text]
editor.go: Utwórz edytor tekstu (odbiorcę) i obiekt wywołujący, który zarządza historią poleceń.Zbuduj strukturę
TextEditorz polemContent(string) oraz metodamiAppend(text string)iDeleteLast(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 wynikUndoLast() 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,deletelubundo). 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 jakoFinal: [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
undoOczekiwany wynik:
Inserted: Hello
Inserted: World
Deleted: rld
Undone delete: rld
Undone insert: World
Final: HelloA 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
TestOczekiwany wynik:
Nothing to undo
Inserted: Test
Final: TestZauważ, ż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)
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików11Biblioteka standardowa i OOP
io.Reader i io.Writersort.InterfaceInterfejs fmt.Stringerencoding/json ze strukturamiInterfejs http.HandlerPowtórzenie – modele REST API14Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuMiddleware jako dekorator3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online