w , ,

FajneFajne Dobre!Dobre! ŚwietnieŚwietnie Że co?Że co? WnerwWnerw

Decorator – prosty sposób na zastąpienie trudnej hierarchii klas

decorator wzorzec projektowy

Decorator (Dektorator) to strukturalny wzorzec projektowy, jest to jeden z najpraktyczniejszych wzorców projektowych. Dlatego sprawdza się on bardzo dobrze w przypadkach gdzie mamy zaawansowaną hierarchię klas modelowych i zależności między nimi.

Decorator – przykład zastosowania

Załóż, że musisz stworzyć aplikacje dla dealera samochodów. W ofercie jest 50 różnych modeli. Każdy z tych modelów ma po 3 wersje wyposażenia.

Decorator - przykład zastosowania wzorca projektowego dla salonu samochodowego
Tworzenie aplikacji dla dealera pojazdów

W rezultacie musisz utworzyć 50 klas modelowych po 3 warianty każda, co daje Ci finalnie 150 klas. Możesz próbować zredukować ich ilość poprzez wykorzystanie dziedziczenia, ale najpewniej zakończy się to bardzo złożonym i nadal obszernym kodem.

Jednak to nie koniec problemów. Dla każdego pokazu musisz uwzględnić dodatki, które klient może sobie zażyczyć – np. podłokietnik, koło zapasowe, dodatkowy zestaw opon. Pierwsze co nasuwa się na myśl, to seria if’ów, którymi będziemy dokonywać sprawdzenia jakie klient dodatki do auta wybrał – jednak czy wyobrażasz sobie tą ogromną ilość instrukcji warunkowych?

Wyzwanie jest niebagatelne, jednak jest to na tyle znana sytuacja problemowa, że ktoś już wcześniej wpadł na jej rozwiązanie i nazwał „Decorator”, który przeszedł do kanonu jako wzorzec projektowy.

Decorator – diagram klas

Decorator - diagram klas
Diagram klas dla wzorca projektowego Decorator

W poszczególne miejsca powstawiajmy teraz konkretne wartości:

  • Component = najbardziej ogólna klasa pojazdu np. Car
  • CreateComponent = przykład modelu dla pojazdu np. Hyundai i20, Hyundai i30, itd..
  • Decorator = klasa dekorująca, czyli wzbogacająca obiekt typu CreateComponent. W naszym przykładzie będzie to CarAccessories (dodatki)
  • ConcreteDecorator – konkretny dodatek do pojazdu np. AirConditioning lub Armrest (podłokietnik)

Dlatego podstawiamy pod diagram konkretne wartości i otrzymujemy:

Decorator - diagram klas z konkretnymi wartościami
Diagram klas dla wzorca projektowego Decorator z konkretnymi wartościami

Decorator – przykład implementacji

Teraz dla drugiego diagramu tworzymy poszczególne klasy.

Car

W pierwszej kolejności prosty przykład klasy Car. Powinna to być klasa abstrakcyjna, ponieważ instancje muszą być tworzone na podstawie klas po niej dziedziczących.

public abstract class Car {
    public abstract float price();
    public abstract String description();
}

i20Car

Następnie tworzymy wszystkie modele dla pojazdów, które dziedziczyć będą po klasie Car. W moim przypadku będzie to tylko i20Car, ale mogą być to kolejne i30Car, itd…

public class i20Car extends Car {
    @Override
    public float price() {
        return 20000;
    }
    @Override
    public String description() {
        return "Hyundai i20";
    }
}

Decorator

Właściwy dekorator, czyli wszystko to co będzie dekorowało poszczególne pojazdy – jest to klasa abstrakcyjna, która dziedziczy po pierwotnej klasie abstrakcyjnej Car, nie potrzebuje ona implementacji.

public abstract class CarAcessoriesDecorator extends Car {}

ConcreteDecorator – Armrest

Podobnie zaimplementujemy najważniejszą klasę dla dodatku. Jednak może być ich dowolna ilość, w moim przypadku będzie to jedna – Armreset.
Przede wszystkim klasa ta musi zawierać konstruktor który który przyjmuje dekorowany obiekt – w tym przypadku instancja typu Car.

public class Armrest extends CarAcessoriesDecorator {

    private Car car;

    public Armrest(Car car) {
        this.car = car;
    }

    @Override
    public float price() {
        return car.price() + 500F;
    }

    @Override
    public String description() {
        return car.description() + " with armrest";
    }
}

Przykład działania wzorca projektowego Decorator

Finalnie tworzymy instancje pojazdu, a następnie możemy ją udekorować:

public static void main(String[] args) {
    Car i20Car = new i20Car();
    System.out.println("i20 price: " + i20Car.price());
    System.out.println("i20 description: " + i20Car.description());

    Car i20withArmrester = new Armrest(i20Car);
    System.out.println("i20withArmrester price: " + i20withArmrester.price());
    System.out.println("i20withArmrester description: " + i20withArmrester.description());
}

Rezultat działania:

i20 price: 20000.0
i20 description: Hyundai i20
i20withArmrester price: 20500.0
i20withArmrester description: Hyundai i20 with armrest

Wzorzec Decorator – wnioski

Podsumowując – uznałem ten wzorzec jako jeden z najbardziej podstawowych, gdyż nie ma innej alternatywy do rozwiązywania podobnie skomplikowanych przypadków. Dlatego zaprezentowałem go w ramach mojego Szkolenia Wzorce Projektowe. Szczególnie dobrze jest go wdrożyć, kiedy mamy wiele klas modelowych i ciężko zapanować nam nad projektem. Warto również zapoznać z tym wzorcem projektowym swojego kolegę, który nagminnie wykorzystuje dziedziczenie 🙂

Napisane przez Przemysław Bykowski

Aktywny programista i energiczny trener. Specjalizuje się w Spring Boot i uczę go w ramach AkademiaSpring.pl. Po godzinach udzielam się na YouTubach. Więcej o mnie.

Dodaj komentarz

Angular dla programistów Java

Angular dla programistów Java. Podobieństwa dzięki, którym szybko się go nauczysz

java14

Java 14 – opis zmian i wideo-szkolenie