Menu
Coddy logo textTech

Duck typing w Go

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

Termin duck typing pochodzi od powiedzenia: „Jeśli chodzi jak kaczka i kwacze jak kaczka, to jest kaczką”. W Go oznacza to, że typ nie musi jawnie deklarować, że implementuje interfejs. Wystarczy, że ma odpowiednie metody.

Rozważ ten interfejs i dwa całkowicie niezwiązane ze sobą typy:

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

W definicjach ani Duck, ani Robot nie ma nigdzie wzmianki o Quacker. Mimo to oba spełniają interfejs, ponieważ mają metodę Quack() o prawidłowej sygnaturze:

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Ta niejawna zgodność jest potężna, ponieważ pozwala definiować interfejsy, gdy typy już istnieją. Możesz utworzyć interfejs pasujący do typów z zewnętrznych pakietów bez modyfikowania ich kodu źródłowego. Typy nie muszą wiedzieć o istnieniu Twojego interfejsu. Wystarczy, że zachowują się poprawnie.

Duck typing w Go zapewnia elastyczność języków dynamicznych, a jednocześnie zachowuje bezpieczeństwo typów na etapie kompilacji. Kompilator sprawdza, czy typy rzeczywiście mają wymagane metody, zanim pozwoli używać ich jako wartości interfejsu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system wiadomości, który pokaże duck typing w praktyce. Utworzysz całkowicie niezwiązane ze sobą typy, które mogą wysyłać wiadomości za pośrednictwem wspólnego interfejsu, mimo że żaden z nich nie deklaruje jawnie, że go implementuje.

Rozmieścisz kod w trzech plikach:

  • messengers.go: Utwórz trzy niezwiązane ze sobą struktury, które przypadkiem mają takie samo zachowanie:
    • Phone z polem Number. Jego metoda SendMessage(text string) string zwraca SMS to [Number]: [text]
    • Computer z polem Email. Jego metoda SendMessage(text string) string zwraca Email to [Email]: [text]
    • Pigeon z polem Name. Jego metoda SendMessage(text string) string zwraca [Name] carries: [text]
    Zauważ, że żaden z tych typów nie wspomina o żadnym interfejsie. Po prostu przypadkiem mają tę samą sygnaturę metody.
  • sender.go: Zdefiniuj interfejs Messenger, który wymaga metody SendMessage(text string) string. Utwórz funkcję o nazwie Broadcast, która przyjmuje wycinek wartości typu Messenger oraz wiadomość w postaci ciągu znaków, a następnie zwraca wycinek ciągów znaków zawierający wynik wywołania SendMessage dla każdego komunikatora.
  • main.go: Odczytaj dane wejściowe, utwórz po jednym obiekcie każdego typu komunikatora, zbierz je w wycinku wartości typu Messenger i użyj Broadcast, aby wysłać wiadomość za pośrednictwem wszystkich komunikatorów. Wypisz każdy wynik w osobnym wierszu.

Podane zostaną następujące dane wejściowe:

  • Wiersz 1: numer telefonu
  • Wiersz 2: adres e-mail
  • Wiersz 3: imię gołębia
  • Wiersz 4: wiadomość do rozesłania

Na przykład dla wartości 555-1234, alice@mail.com, Winston i Hello World wynik powinien wyglądać następująco:

SMS to 555-1234: Hello World
Email to alice@mail.com: Hello World
Winston carries: Hello World

Kluczowy wniosek jest taki, że Phone, Computer i Pigeon nie mają ze sobą nic wspólnego. Nie współdzielą typu bazowego ani jawnie nie implementują interfejsu Messenger. Jednak ponieważ wszystkie „kwaczą” tak samo (mają zgodną metodę), Go pozwala używać ich jako wartości typu Messenger. Tak właśnie działa duck typing.

Spróbuj swoich sił

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Wczytaj numer telefonu
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Wczytaj adres e-mail
	scanner.Scan()
	email := scanner.Text()
	
	// Wczytaj imię gołębia
	scanner.Scan()
	pigeonName := scanner.Text()
	
	// Wczytaj wiadomość do rozesłania
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Utwórz po jednym obiekcie każdego typu posłańca (Phone, Computer, Pigeon)
	
	// TODO: Zbierz wszystkich posłańców w slice typu Messenger
	
	// TODO: Użyj Broadcast, aby wysłać wiadomość za pośrednictwem wszystkich posłańców
	
	// TODO: Wypisz każdy wynik w osobnym wierszu
	
	// Kod tymczasowy wykorzystujący zmienne (usuń podczas implementacji)
	_ = phoneNumber
	_ = email
	_ = pigeonName
	_ = message
}
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