Menu
Coddy logo textTech

Implementacja niejawna

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

W wielu językach obiektowych, takich jak Java czy C#, musisz jawnie zadeklarować, że typ implementuje interfejs, używając słów kluczowych takich jak implements. Go podchodzi do tego inaczej: implementacja interfejsu jest niejawna.

Typ spełnia wymagania interfejsu po prostu przez zaimplementowanie wszystkich jego metod. Nie trzeba nigdzie deklarować tej relacji. Kompilator ustala ją automatycznie.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter niejawnie implementuje Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Zauważ, że w FileWriter nie ma deklaracji implements Writer. Ponieważ FileWriter ma metodę Write(data string) int zgodną z sygnaturą interfejsu, automatycznie spełnia wymagania interfejsu Writer.

Ten projekt ma potężną konsekwencję: możesz definiować interfejsy, gdy typy już istnieją. Jeśli biblioteka innej firmy ma typ z metodami, których potrzebujesz, możesz utworzyć interfejs, który ten typ już spełnia, bez modyfikowania oryginalnego kodu.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Działa, ponieważ FileWriter spełnia wymagania interfejsu Writer
}

Implementacja niejawna sprawia, że kod Go jest luźno powiązany i elastyczny. Typy nie muszą z wyprzedzeniem znać interfejsów, co ułatwia komponowanie systemów z niezależnych części.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system dostarczania wiadomości, który pokazuje, jak typy w Go niejawnie spełniają interfejsy. Utworzysz różne typy komunikatorów, które wszystkie spełniają ten sam interfejs, bez jawnego deklarowania tej relacji.

Podzielisz kod na dwa pliki:

  • messengers.go: Zdefiniuj interfejs Messenger z jedną metodą Send(message string) string. Następnie utwórz dwie struktury, które będą niejawnie spełniać ten interfejs:
    • EmailMessenger z polem Address (string)
    • SMSMessenger z polem PhoneNumber (string)
    Każda struktura powinna mieć metodę Send, która zwraca ciąg znaków opisujący dostarczenie wiadomości. Żadna ze struktur nie powinna jawnie deklarować, że implementuje Messenger: Go ustali to automatycznie na podstawie sygnatur metod.
  • main.go: Utwórz funkcję o nazwie DeliverMessage, która przyjmuje dowolny element typu Messenger oraz ciąg znaków z wiadomością, a następnie zwraca wynik wywołania Send. Odczytaj informacje kontaktowe z wejścia, utwórz oba typy komunikatorów i użyj swojej funkcji, aby dostarczyć wiadomości za pośrednictwem każdego z nich.

Zostaną podane następujące dane wejściowe:

  • Wiersz 1: Adres e-mail
  • Wiersz 2: Numer telefonu
  • Wiersz 3: Wiadomość do wysłania

Metody Send powinny zwracać ciągi znaków w następujących formatach:

  • EmailMessenger: Email to [Address]: [message]
  • SMSMessenger: SMS to [PhoneNumber]: [message]

Na przykład dla danych alice@example.com, 555-1234 i Hello! wynik powinien wyglądać następująco:

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

Najważniejsza kwestia jest taka, że funkcja DeliverMessage przyjmuje dowolny element typu Messenger, a zarówno EmailMessenger, jak i SMSMessenger spełniają ten interfejs po prostu dzięki posiadaniu zgodnej metody Send. Nigdzie nie ma słowa kluczowego implements: niejawna implementacja w Go obsługuje wszystko.

Spróbuj swoich sił

package main

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

// TODO: Utwórz funkcję DeliverMessage, która przyjmuje dowolny Messenger
// oraz ciąg znaków z wiadomością, a następnie zwraca wynik wywołania Send

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Odczytaj adres e-mail
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// Odczytaj numer telefonu
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// Odczytaj wiadomość
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: Utwórz EmailMessenger z adresem e-mail
	
	// TODO: Utwórz SMSMessenger z numerem telefonu
	
	// TODO: Użyj DeliverMessage, aby wysłać wiadomość za pośrednictwem każdego obiektu Messenger
	// i wypisz wyniki
}
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