Menu
Coddy logo textTech

Osadzanie a agregacja

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

Dowiedziałeś się, że osadzanie udostępnia pola i metody w zewnętrznej strukturze. Go obsługuje jednak również agregację, w której dołączasz inną strukturę jako nazwane pole. Zrozumienie, kiedy używać każdego z tych podejść, jest kluczowe dla dobrego projektowania.

Przy osadzaniu pomijasz nazwę pola, a składowe typu wewnętrznego stają się bezpośrednio dostępne:

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // osadzanie — bez nazwy pola
}

d := Document{}
d.Write()  // bezpośredni dostęp

W przypadku agregacji nadajesz polu jawną nazwę, która jest wymagana do uzyskania dostępu do typu wewnętrznego:

type Document struct {
    writer Writer  // agregacja — pole nazwane
}

d := Document{writer: Writer{}}
d.writer.Write()  // trzeba użyć nazwy pola

Kluczowa różnica polega na relacji, którą wyrażasz. Dziedziczenie sugeruje relację „jest” (is-a), w której typ zewnętrzny przyjmuje tożsamość typu wewnętrznego. Agregacja sugeruje relację „ma” (has-a), w której typ zewnętrzny jedynie zawiera typ wewnętrzny.

Użyj osadzania, gdy chcesz, aby typ zewnętrzny spełniał interfejsy zaimplementowane przez typ wewnętrzny lub gdy bezpośredni dostęp do metod sprawia, że API jest czytelniejsze. Użyj agregacji, gdy typ wewnętrzny jest szczegółem implementacyjnym, który nie powinien być udostępniany, lub gdy chcesz mieć jawny wpływ na sposób dostępu do typu wewnętrznego.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system generowania raportów, który pokaże, kiedy używać osadzania, a kiedy agregacji. Utworzysz system, w którym niektóre komponenty powinny być bezpośrednio dostępne (osadzanie), a inne powinny pozostać wewnętrznymi szczegółami implementacji (agregacja).

Podzielisz kod na trzy pliki:

  • formatter.go: Utwórz strukturę Formatter z polem Style (string) oraz metodą Format(text string) string, która zwraca tekst otoczony określonym stylem, na przykład [Style] text [/Style]. Będzie to wewnętrzna funkcja pomocnicza, która nie powinna być bezpośrednio udostępniana.
  • report.go: Utwórz dwie struktury, które obsługują generowanie raportów na różne sposoby:
    • Strukturę Metadata z polami Author i Date. Reprezentuje ona publiczne informacje, które powinny być bezpośrednio dostępne
    • Strukturę Report z polem Title, która osadza Metadata (aby zapewnić bezpośredni dostęp do autora i daty), ale używa agregacji dla Formatter (jako wewnętrznego szczegółu implementacji z nazwanym polem formatter)
    Dodaj do Report metodę Generate() string, która używa wewnętrznego formatera do formatowania tytułu i uwzględnia metadane.
  • main.go: Odczytaj szczegóły raportu z wejścia, utwórz Report z osadzonymi metadanymi i agregowanym formatterem. Pokaż różnicę, uzyskując dostęp do autora bezpośrednio przez raport (za pomocą osadzania), a następnie generując sformatowany raport (który wewnętrznie używa ukrytego formatera).

Dane wejściowe będą podane w następującej kolejności:

  • Wiersz 1: Tytuł raportu
  • Wiersz 2: Imię i nazwisko autora
  • Wiersz 3: Data
  • Wiersz 4: Styl formatera

Twoja metoda Format powinna zwracać:

[Style] text [/Style]

Twoja metoda Generate powinna zwracać:

[formatted title]
Author: [Author], Date: [Date]

W pliku głównym wypisz trzy wiersze:

  1. Autora pobranego bezpośrednio z Report (pokazując, że osadzanie udostępnia pole)
  2. Datę pobraną za pośrednictwem nazwy osadzonego typu (report.Metadata.Date)
  3. Wynik wywołania Generate()

Na przykład dla danych Quarterly Results, Jane Doe, 2024-01-15 i BOLD wynikiem powinno być:

Jane Doe
2024-01-15
[BOLD] Quarterly Results [/BOLD]
Author: Jane Doe, Date: 2024-01-15

Zwróć uwagę, że dostęp do Author i Date jest możliwy bezpośrednio przez Report dzięki osadzaniu, natomiast formatter pozostaje ukryty. Nie możesz bezpośrednio wywołać report.Format(), ponieważ jest on agregowany za pomocą nazwanego pola. To praktyczny przykład relacji „jest” i „ma”.

Spróbuj swoich sił

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	title := scanner.Text()
	
	scanner.Scan()
	author := scanner.Text()
	
	scanner.Scan()
	date := scanner.Text()
	
	scanner.Scan()
	style := scanner.Text()
	
	// TODO: Utwórz strukturę Metadata z autorem i datą
	
	// TODO: Utwórz Formatter z podanym stylem
	
	// TODO: Utwórz Report, który osadza Metadata i agreguje Formatter
	
	// TODO: Wypisz autora, odwołując się do niego bezpośrednio przez Report (osadzenie udostępnia pole)
	
	// TODO: Wypisz datę, odwołując się do niej przez nazwę osadzonego typu (report.Metadata.Date)
	
	// TODO: Wypisz wynik wywołania Generate()
	
	// Wyłącz ostrzeżenia o nieużywanych zmiennych (usuń te instrukcje, gdy zaczniesz używać zmiennych)
	_ = title
	_ = author
	_ = date
	_ = style
}
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