Wzorzec adaptera
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 92 z 107.
Wzorzec Adapter umożliwia współpracę niekompatybilnych interfejsów poprzez opakowanie istniejącego typu w nowy interfejs. Podczas gdy Command enkapsuluje działania, Adapter tłumaczy jeden interfejs na inny, którego oczekują klienci.
Wyobraź sobie, że masz starszą drukarkę, która używa innej sygnatury metody niż ta, której oczekuje Twoja aplikacja:
// Interfejs docelowy używany przez Twoją aplikację
type Printer interface {
Print(text string) string
}
// Starsza drukarka z niezgodnym interfejsem
type OldPrinter struct{}
func (o OldPrinter) PrintDocument(doc string, copies int) string {
return fmt.Sprintf("Old printer: %s (x%d)", doc, copies)
}Adapter opakowuje starszy typ i implementuje oczekiwany interfejs:
type OldPrinterAdapter struct {
oldPrinter OldPrinter
}
func (a OldPrinterAdapter) Print(text string) string {
return a.oldPrinter.PrintDocument(text, 1)
}Teraz stara drukarka działa bezproblemowo z kodem oczekującym interfejsu Printer:
func PrintMessage(p Printer, msg string) string {
return p.Print(msg)
}
adapter := OldPrinterAdapter{oldPrinter: OldPrinter{}}
result := PrintMessage(adapter, "Hello") // "Old printer: Hello (x1)"Wzorzec Adapter jest nieoceniony podczas integrowania bibliotek innych firm, pracy ze starszym kodem lub gdy musisz użyć istniejących klas, które nie spełniają wymagań Twojego interfejsu. Działa jak most, nie modyfikując oryginalnego kodu.
Wyzwanie
ŁatwyZbudujmy system odtwarzacza multimedialnego z użyciem wzorca Adapter! Masz nowoczesny odtwarzacz audio, który oczekuje ujednoliconego interfejsu, ale musisz zintegrować starszy sprzęt audio, który używa zupełnie innych sygnatur metod. Twój adapter wypełni tę lukę, umożliwiając staremu sprzętowi bezproblemową współpracę z nowym systemem.
Rozmieścisz swój kod w trzech plikach:
player.go: Zdefiniuj docelowy interfejs i starsze urządzenie audio.Utwórz interfejs
AudioPlayer, którego oczekuje nowoczesny system, z metodąPlay(track string) string, która przyjmuje nazwę utworu i zwraca komunikat o odtwarzaniu.Zdefiniuj także starsze urządzenie, które nie pasuje do tego interfejsu:
- Struktura
VinylPlayerz polemSpeedRPM(int). Ma metodęDropNeedle(record string, side string) string, która zwracaPlaying [record] ([side] side) at [speed]rpm
Interfejs VinylPlayer jest niezgodny z AudioPlayer. Wymaga dwóch parametrów i używa zupełnie innej nazwy metody.
- Struktura
adapter.go: Utwórz adapter, który zapewni zgodność starszego urządzenia.Zbuduj strukturę
VinylAdapter, która opakowujeVinylPlayeri implementuje interfejsAudioPlayer. Gdy wywołana zostanie metodaPlay, adapter powinien wywołać metodęDropNeedleodtwarzacza winylowego, domyślnie ustawiającAjako wartość parametru strony.Utwórz także nowoczesny odtwarzacz do porównania:
- Struktura
DigitalPlayer: jej metodaPlayzwracaStreaming: [track]
- Struktura
main.go: Pokaż, że oba odtwarzacze działają przez ten sam interfejs.Utwórz funkcję
PlayMusic(player AudioPlayer, track string) string, która używa interfejsu do odtwarzania muzyki. Ta funkcja nie powinna wiedzieć ani przejmować się tym, czy używa odtwarzacza cyfrowego, czy odtwarzacza winylowego z adapterem.Wczytaj typ odtwarzacza (
digitallubvinyl), a jeśli jest to odtwarzacz winylowy, wczytaj także prędkość RPM. Następnie wczytaj nazwę utworu. Utwórz odpowiedni odtwarzacz (używając adaptera dla odtwarzacza winylowego), odtwórz utwór za pomocą funkcjiPlayMusici wypisz wynik.
Podane zostaną następujące dane wejściowe:
- Linia 1: Typ odtwarzacza (
digitallubvinyl) - Linia 2: Prędkość RPM (tylko w przypadku odtwarzacza winylowego)
- Ostatnia linia: Nazwa utworu do odtworzenia
Na przykład, dla danych wejściowych:
digital
Bohemian RhapsodyWynik powinien być następujący:
Streaming: Bohemian RhapsodyA dla danych wejściowych:
vinyl
33
Abbey RoadWynik powinien być następujący:
Playing Abbey Road (A side) at 33rpmA dla danych wejściowych:
vinyl
45
Blue MondayWynik powinien być następujący:
Playing Blue Monday (A side) at 45rpmZwróć uwagę, że funkcja PlayMusic działa identycznie z oboma odtwarzaczami. Nie wie, że odtwarzacz winylowy korzysta w rzeczywistości z adaptera dla zupełnie innego interfejsu. Adapter w tle tłumaczy wywołania nowoczesnego interfejsu na starszy format!
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// PlayMusic używa interfejsu AudioPlayer do odtwarzania muzyki
// Ta funkcja działa z każdą implementacją AudioPlayer
// TODO: Zaimplementuj tę funkcję tak, aby wywoływała player.Play(track) i zwracała wynik
func PlayMusic(player AudioPlayer, track string) string {
// TODO: Zaimplementuj tę funkcję
return ""
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Odczytaj typ odtwarzacza
playerType, _ := reader.ReadString('\n')
playerType = strings.TrimSpace(playerType)
var player AudioPlayer
var track string
if playerType == "vinyl" {
// Odczytaj prędkość RPM dla płyty winylowej
rpmStr, _ := reader.ReadString('\n')
rpmStr = strings.TrimSpace(rpmStr)
rpm, _ := strconv.Atoi(rpmStr)
// Odczytaj nazwę utworu
track, _ = reader.ReadString('\n')
track = strings.TrimSpace(track)
// TODO: Utwórz VinylPlayer z podaną wartością RPM
// TODO: Opakuj go w VinylAdapter
// TODO: Przypisz do zmiennej player
_ = rpm // Usuń tę linię, gdy zaczniesz używać rpm
} else {
// Odczytaj nazwę utworu
track, _ = reader.ReadString('\n')
track = strings.TrimSpace(track)
// TODO: Utwórz DigitalPlayer
// TODO: Przypisz do zmiennej player
}
// TODO: Wywołaj PlayMusic i wypisz wynik
_ = player // Usuń tę linię, gdy użyjesz player
_ = track // Usuń tę linię, gdy użyjesz track
}
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