Wzorzec Strategy
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 90 z 107.
Wzorzec strategii pozwala zdefiniować rodzinę algorytmów, enkapsulować każdy z nich i umożliwić ich wzajemną wymienność w trakcie działania programu. W przeciwieństwie do wzorca Observer, w którym wiele obiektów reaguje na jedno zdarzenie, wzorzec Strategy pozwala pojedynczemu obiektowi dynamicznie przełączać się między różnymi zachowaniami.
W Go implementujemy wzorzec Strategy, definiując interfejs dla algorytmu i wstrzykując różne implementacje do struktury kontekstu:
type PaymentStrategy interface {
Pay(amount float64) string
}
type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}
type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via PayPal", amount)
}Struktura kontekstu przechowuje odwołanie do interfejsu strategii, dzięki czemu można zmienić algorytm bez modyfikowania kodu kontekstu:
type Checkout struct {
strategy PaymentStrategy
}
func (c *Checkout) SetStrategy(s PaymentStrategy) {
c.strategy = s
}
func (c *Checkout) ProcessPayment(amount float64) string {
return c.strategy.Pay(amount)
}Teraz możesz zmienić metodę płatności w czasie działania programu:
checkout := &Checkout{}
checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00)) // Zapłacono 100.00 kartą kredytową
checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00)) // Zapłacono 50.00 przez PayPalWzorzec strategii sprawdza się idealnie, gdy masz kilka sposobów wykonania operacji i musisz wybrać jeden na podstawie danych wejściowych użytkownika, konfiguracji lub warunków w czasie działania. Typowe zastosowania obejmują algorytmy sortowania, metody kompresji i reguły walidacji.
Wyzwanie
ŁatwyZbudujmy system formatowania tekstu, używając wzorca Strategia! Utworzysz różne strategie formatowania, które można zamieniać w czasie działania programu, dzięki czemu ten sam procesor tekstu będzie generować różne style wyjściowe bez zmiany jego podstawowego kodu.
Rozmieścisz kod w trzech plikach:
formatter.go: Zdefiniuj interfejs strategii i konkretne strategie formatowania.Utwórz interfejs
TextFormatterz metodąFormat(text string) string, która przekształca tekst zgodnie z regułami strategii.Zaimplementuj trzy strategie formatowania:
UppercaseFormatter: zamienia cały tekst na wielkie literySnakeCaseFormatter: zamienia spacje na podkreślenia i zapisuje tekst małymi literamiTitleFormatter: zamienia pierwszą literę każdego słowa na wielką (słowa są oddzielone spacjami)
processor.go: Utwórz strukturę kontekstu, która używa strategii formatowania.Utwórz strukturę
TextProcessor, która przechowuje strategięTextFormatter. Dodaj następujące metody:SetFormatter(f TextFormatter), aby zmieniać aktywną strategię formatowaniaProcess(text string) string, która stosuje bieżący formater do tekstu
Utwórz także konstruktor
NewTextProcessor(), który zwraca procesor bez ustawionego początkowego formatera.main.go: Pokaż, jak zamieniać strategie w czasie działania programu.Wczytaj tekst, a następnie liczbę operacji formatowania. Dla każdej operacji wczytaj typ formatowania (
upper,snakelubtitle), ustaw odpowiedni formater w procesorze, przetwórz oryginalny tekst i wypisz wynik.
Podane zostaną następujące dane wejściowe:
- Wiersz 1: tekst do sformatowania
- Wiersz 2: liczba operacji formatowania
- Kolejne wiersze: typ formatowania dla każdej operacji
Na przykład, dla danych:
hello world today
3
upper
snake
titleWynik powinien być następujący:
HELLO WORLD TODAY
hello_world_today
Hello World TodayA dla danych:
Go Programming Language
2
snake
upperWynik powinien być następujący:
go_programming_language
GO PROGRAMMING LANGUAGEA dla danych:
design patterns are useful
1
titleWynik powinien być następujący:
Design Patterns Are UsefulZwróć uwagę, że ten sam TextProcessor generuje zupełnie różne wyniki tylko dzięki zmianie strategii formatera. Procesor nie wie ani nie musi wiedzieć, którego konkretnego formatera używa. Po prostu deleguje działanie do aktualnie ustawionej strategii!
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Wczytaj tekst do sformatowania
text, _ := reader.ReadString('\n')
text = strings.TrimSpace(text)
// Wczytaj liczbę operacji
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Utwórz nowy procesor tekstu
processor := NewTextProcessor()
// Wykonaj każdą operację formatowania
for i := 0; i < count; i++ {
formatType, _ := reader.ReadString('\n')
formatType = strings.TrimSpace(formatType)
// TODO: Na podstawie formatType ("upper", "snake" lub "title"),
// ustaw odpowiedni formater w procesorze
// i wypisz przetworzony tekst
}
}
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 kolekcja13Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec Abstract FactoryWzorzec ObserverWzorzec Strategy2Typy 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ów3Wskaź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