Menu
Coddy logo textTech

Wzorzec dekoratora

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 72 z 87.

Wzorzec dekoratora to strukturalny wzorzec projektowy, który pozwala dodawać obiektom nowe zachowania poprzez opakowywanie ich w specjalne obiekty nazywane dekoratorami. W przeciwieństwie do dziedziczenia, które dodaje zachowanie w czasie kompilacji, dekoratory dodają funkcjonalność dynamicznie, w czasie działania programu, bez modyfikowania oryginalnej klasy.

Wyobraź sobie kawiarnię, w której zaczynasz od podstawowej kawy i możesz dodać dodatki, takie jak mleko, cukier lub bita śmietana. Każdy dodatek otacza oryginalną kawę, zwiększając jej koszt i rozszerzając opis. Wzorzec wykorzystuje wspólny interfejs, dzięki czemu udekorowane obiekty można traktować tak samo jak oryginalny obiekt:

interface Coffee {
    String getDescription();
    double getCost();
}

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }
    public double getCost() {
        return 2.0;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    public String getDescription() {
        return coffee.getDescription() + ", Milk";
    }
    
    public double getCost() {
        return coffee.getCost() + 0.5;
    }
}

Dekorator przechowuje referencję do opakowanego obiektu i przekazuje mu wywołania, dodając jednocześnie własne zachowanie. Możesz łączyć wiele dekoratorów, aby zestawiać różne funkcje:

Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);

System.out.println(order.getDescription());  // Prosta kawa, mleko, cukier
System.out.println(order.getCost());         // 2.75

Wzorzec Dekoratora sprawdza się, gdy chcesz dodawać obiektom odpowiedzialności bez tworzenia ogromnej liczby podklas. Każdy dekorator jest niezależny, więc możesz swobodnie je łączyć, aby uzyskać dokładnie taką kombinację zachowań, jakiej potrzebujesz.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system powiadomień z użyciem wzorca Dekorator! Utworzysz podstawowe powiadomienie, które można rozszerzać o różne kanały dostarczania: alerty e-mail, alerty SMS i powiadomienia push, opakowując dekoratory jeden w drugi. Odzwierciedla to rzeczywiste systemy, w których pojedyncza wiadomość może być jednocześnie wysyłana wieloma kanałami.

Rozmieścisz kod w czterech plikach:

  • Notifier.java: Zdefiniuj interfejs Notifier, który pełni funkcję interfejsu komponentu we wzorcu Dekorator. Powinien deklarować metodę send(String message), która obsługuje wysyłanie powiadomień.
  • BasicNotifier.java: Utwórz konkretną klasę komponentu implementującą interfejs Notifier. Reprezentuje ona najprostszą formę powiadomienia: alert w aplikacji. Gdy zostanie wywołana metoda send, klasa powinna wypisać In-App: [message].
  • NotifierDecorators.java: Zbuduj tutaj hierarchię dekoratorów. Zacznij od abstrakcyjnej klasy NotifierDecorator, która implementuje interfejs Notifier i przechowuje referencję do opakowanego obiektu Notifier (przekazanego przez konstruktor).

    Następnie utwórz trzy konkretne dekoratory rozszerzające klasę NotifierDecorator:

    EmailDecorator — gdy zostanie wywołana metoda send, najpierw przekazuje wywołanie do opakowanego powiadamiacza, a następnie wypisuje Email: [message].

    SMSDecorator — gdy zostanie wywołana metoda send, najpierw przekazuje wywołanie do opakowanego powiadamiacza, a następnie wypisuje SMS: [message].

    PushDecorator — gdy zostanie wywołana metoda send, najpierw przekazuje wywołanie do opakowanego powiadamiacza, a następnie wypisuje Push: [message].

  • Main.java: Połącz swój system dekoratorów! Otrzymasz dwa wejścia: wiadomość powiadomienia (String) oraz rozdzieloną przecinkami listę kanałów do dodania (String), na przykład "email,sms" lub "push,email,sms".

    Zacznij od BasicNotifier. Następnie, na podstawie wejścia zawierającego kanały, opakuj go odpowiednimi dekoratorami w kolejności, w jakiej występują. Prawidłowe nazwy kanałów to email, sms i push.

    Na koniec wywołaj send z wiadomością na w pełni udekorowanym powiadamiaczu.

Otrzymasz kolejno dwa wejścia: wiadomość do wysłania (String) oraz kanały do włączenia (String, rozdzielony przecinkami).

Na przykład dla wejść Server is down! i email,push wynikiem będzie:

In-App: Server is down!
Email: Server is down!
Push: Server is down!

Zwróć uwagę, że każdy dekorator dodaje własne działanie, zachowując oryginalne powiadomienie. Kolejność opakowywania określa kolejność wyjścia: najpierw działa najbardziej wewnętrzny komponent (BasicNotifier), a następnie każdy dekorator dodaje swoją warstwę. Możesz dowolnie łączyć kanały, nie tworząc osobnych klas dla każdej kombinacji!

Spróbuj swoich sił

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Wczytaj wiadomość do wysłania
        String message = scanner.nextLine();
        
        // Wczytaj listę kanałów rozdzielonych przecinkami (np. "email,sms" lub "push,email,sms")
        String channelsInput = scanner.nextLine();
        
        // TODO: Zacznij od BasicNotifier
        
        // TODO: Podziel dane wejściowe kanałów według przecinków
        
        // TODO: Przejdź przez każdy kanał i opakuj obiekt notifier odpowiednim dekoratorem
        // Poprawne kanały to: "email", "sms", "push"
        
        // TODO: Wywołaj send na obiekcie notifier opakowanym wszystkimi dekoratorami, przekazując wiadomość
        
    }
}
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 Java online