Menu
Coddy logo textTech

Wzorzec dekoratora

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

Wzorzec Dekorator pozwala dynamicznie dodawać nowe zachowania do obiektów poprzez opakowywanie ich innymi obiektami. Adapter zmienia interfejs, aby dopasować go do innego, natomiast Dekorator zachowuje ten sam interfejs, ale rozszerza funkcjonalność, nakładając warstwy opakowań na oryginalny obiekt.

W Go implementujemy to, sprawiając, że zarówno typ bazowy, jak i dekoratory implementują ten sam interfejs:

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

func (b BasicNotifier) Send(message string) string {
    return "Sending: " + message
}

Dekoratory opakowują inny obiekt Notifier i dodają zachowanie przed przekazaniem mu zadania lub po nim:

type TimestampDecorator struct {
    wrapped Notifier
}

func (t TimestampDecorator) Send(message string) string {
    timestamped := "[2024-01-15] " + message
    return t.wrapped.Send(timestamped)
}

type UppercaseDecorator struct {
    wrapped Notifier
}

func (u UppercaseDecorator) Send(message string) string {
    return strings.ToUpper(u.wrapped.Send(message))
}

Siła wzorca Decorator tkwi w nakładaniu na siebie wielu opakowań, aby łączyć zachowania:

notifier := BasicNotifier{}
withTimestamp := TimestampDecorator{wrapped: notifier}
withBoth := UppercaseDecorator{wrapped: withTimestamp}

fmt.Println(notifier.Send("Hello"))
// Sending: Hello

fmt.Println(withBoth.Send("Hello"))
// SENDING: [2024-01-15] HELLO

Wzorzec Dekorator sprawdza się idealnie, gdy potrzebujesz dodawać obiektom odpowiedzialności bez modyfikowania ich kodu, zwłaszcza gdy potrzebne są różne kombinacje funkcji. Jest powszechnie używany w warstwach logowania, buforowania, uwierzytelniania i kompresji.

challenge icon

Wyzwanie

Łatwy

Stwórzmy system zamawiania w kawiarni, wykorzystując wzorzec Dekorator! Utworzysz bazowy napój kawowy oraz dekoratory dodające dodatki, takie jak mleko, cukier i bita śmietana. Każdy dekorator opakowuje poprzedni, rozbudowując zarówno opis, jak i cenę — to doskonały przykład z życia pokazujący, jak dekoratory nakładają na siebie funkcjonalności.

Rozmieścisz swój kod w trzech plikach:

  • beverage.go: Zdefiniuj podstawowy interfejs i typ bazowej kawy.

    Utwórz interfejs Beverage z dwiema metodami:

    • Description() string: zwraca informację, jaki to napój
    • Cost() float64: zwraca cenę

    Zaimplementuj strukturę Coffee jako bazowy napój. Jej opis powinien mieć wartość Coffee, a cena 2.00.

  • decorators.go: Utwórz typy dekoratorów dodających dodatki do dowolnego napoju.

    Każdy dekorator opakowuje Beverage i implementuje ten sam interfejs. Utwórz trzy dekoratory:

    • MilkDecorator: dodaje , Milk do opisu i 0.50 do ceny
    • SugarDecorator: dodaje , Sugar do opisu i 0.25 do ceny
    • WhipDecorator: dodaje , Whip do opisu i 0.75 do ceny

    Każdy dekorator powinien wywoływać metody opakowanego napoju i wzbogacać ich wynik.

  • main.go: Twórz spersonalizowane napoje, nakładając dekoratory jeden na drugi.

    Wczytaj liczbę dodatków do dodania. Następnie dla każdego dodatku wczytaj jego typ (milk, sugar lub whip) i opakuj aktualny napój odpowiednim dekoratorem.

    Po zastosowaniu wszystkich dodatków wypisz opis i cenę końcowego napoju w osobnych wierszach. Sformatuj cenę jako $X.XX, z dokładnie dwoma miejscami po przecinku.

Otrzymasz następujące dane wejściowe:

  • Wiersz 1: liczba dodatków do dodania
  • Kolejne wiersze: jeden typ dodatku w każdym wierszu (milk, sugar lub whip)

Na przykład dla danych:

2
milk
sugar

Wynik powinien być następujący:

Coffee, Milk, Sugar
$2.75

A dla danych:

3
whip
milk
milk

Wynik powinien być następujący:

Coffee, Whip, Milk, Milk
$3.75

A dla danych:

0

Wynik powinien być następujący:

Coffee
$2.00

Zwróć uwagę, jak każdy dekorator opakowuje poprzedni napój, rozbudowując zarówno opis, jak i cenę. Możesz dodawać ten sam dodatek wiele razy (na przykład podwójną porcję mleka), a kolejność dekoratorów określa kolejność dodatków w opisie. Bazowa kawa nie wie nic o dodatkach — każdy dekorator po prostu wzbogaca to, co opakowuje!

Spróbuj swoich sił

package main

import (
	"fmt"
)

func main() {
	// Wczytaj liczbę dodatków
	var count int
	fmt.Scanln(&count)

	// Zacznij od podstawowej kawy
	var drink Beverage = &Coffee{}

	// Wczytaj każdy dodatek i opakuj napój odpowiednim dekoratorem
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: W zależności od rodzaju dodatku opakuj drink odpowiednim dekoratorem
		// Użyj MilkDecorator dla "milk"
		// Użyj SugarDecorator dla "sugar"
		// Użyj WhipDecorator dla "whip"
	}

	// TODO: Wypisz końcowy opis i koszt
	// Sformatuj koszt jako $X.XX z dokładnie dwoma miejscami po przecinku
}
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