Menu
Coddy logo textTech

Polimorfizm za pomocą interfejsów

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

Polimorfizm pozwala traktować różne typy w jednolity sposób za pomocą wspólnego interfejsu. W Go osiąga się to wyłącznie za pomocą interfejsów, bez dziedziczenia ani hierarchii klas.

Gdy funkcja przyjmuje typ interfejsu jako parametr, można jej przekazać dowolny typ konkretny, który implementuje ten interfejs. Funkcja nie musi znać konkretnego typu. Interesuje ją tylko zachowanie zdefiniowane przez interfejs:

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

Teraz MakeSound działa z każdym typem, który ma metodę Speak():

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Hau!
    MakeSound(cat)  // Miau!
}

To samo wywołanie funkcji daje różne rezultaty w zależności od przekazanego rzeczywistego typu. To właśnie polimorfizm w działaniu. Funkcja MakeSound jest napisana raz, ale działa z nieograniczoną liczbą typów, o ile spełniają one interfejs Speaker.

To podejście sprawia, że Twój kod jest elastyczny i łatwy do rozbudowy. Dodanie nowego typu, który mówi, nie wymaga zmian w istniejących funkcjach — wystarczy zaimplementować interfejs, a wszystko będzie działać automatycznie.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system opisu pojazdów, który pokaże polimorfizm w działaniu. Utworzysz różne typy pojazdów, które współdzielą wspólne zachowanie za pośrednictwem interfejsu, a następnie napiszesz jedną funkcję działającą z dowolnym pojazdem.

Rozmieścisz kod w dwóch plikach:

  • vehicles.go: Zdefiniuj interfejs Describer, który wymaga metody Describe() string. Następnie utwórz trzy typy pojazdów, z których każdy implementuje ten interfejs na swój sposób:
    • Car z polami Brand i Model. Jego metoda Describe() zwraca Car: [Brand] [Model]
    • Motorcycle z polami Brand i EngineCC (int). Jego metoda Describe() zwraca Motorcycle: [Brand] [EngineCC]cc
    • Bicycle z polem Type (np. „Mountain” lub „Road”). Jego metoda Describe() zwraca Bicycle: [Type]
  • main.go: Utwórz funkcję o nazwie PrintDescription, która przyjmuje dowolny obiekt implementujący Describer i wyświetla wynik wywołania Describe(). Odczytaj z wejścia szczegóły pojazdów, utwórz po jednym obiekcie każdego typu pojazdu i przekaż każdy z nich do PrintDescription, aby pokazać, że ta sama funkcja działa z wszystkimi trzema różnymi typami.

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

  • Wiersz 1: Marka samochodu
  • Wiersz 2: Model samochodu
  • Wiersz 3: Marka motocykla
  • Wiersz 4: Pojemność silnika motocykla w CC (liczba całkowita)
  • Wiersz 5: Typ roweru

Na przykład dla danych Toyota, Camry, Honda, 600 i Mountain wynik powinien wyglądać następująco:

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

Zauważ, że PrintDescription nie musi wiedzieć, czy otrzymuje obiekt typu Car, Motorcycle czy Bicycle. Po prostu wywołuje Describe(), a każdy typ zwraca własny, unikalny wynik. To właśnie polimorfizm: jedna funkcja, wiele zachowań.

Spróbuj swoich sił

package main

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

// TODO: Utwórz funkcję o nazwie PrintDescription, która przyjmuje dowolny obiekt typu Describer
// i wypisuje wynik wywołania Describe()

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Odczytaj dane samochodu
	carBrand, _ := reader.ReadString('\n')
	carBrand = strings.TrimSpace(carBrand)
	carModel, _ := reader.ReadString('\n')
	carModel = strings.TrimSpace(carModel)

	// Odczytaj dane motocykla
	motoBrand, _ := reader.ReadString('\n')
	motoBrand = strings.TrimSpace(motoBrand)
	motoEngineStr, _ := reader.ReadString('\n')
	motoEngineStr = strings.TrimSpace(motoEngineStr)
	motoEngine, _ := strconv.Atoi(motoEngineStr)

	// Odczytaj dane roweru
	bicycleType, _ := reader.ReadString('\n')
	bicycleType = strings.TrimSpace(bicycleType)

	// TODO: Utwórz obiekty Car, Motorcycle i Bicycle, używając wartości wejściowych

	// TODO: Wywołaj PrintDescription dla każdego pojazdu, aby zademonstrować polimorfizm
	fmt.Println("TODO: Print vehicle descriptions")
}
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