Wzorzec Decorator jest wzorcem strukturalnym. Jego rolą jest nadawanie obiektowi nowych funkcjonalności ale jednocześnie zapobiec eksplozji klas, która mogłaby w takiej sytuacji wystąpić. Drugą zaletą dekoratorów jest to, że możemy zmieniać funkcjonalność dynamicznie w trakcie działania programu.
Dekoratory implementują ten sam interfejs co klasa, której funkcjonalność mają rozszerzyć i przyjmuja oryginalny obiekt jako parametr konstruktora. Wszystkie metody są delegowane do obiektu ‚wyżej’ i dodatkowo wykonywane nowe funkcjonalności.
Przykład:
Zaprojektujemy system deserów, gdzie każdy z deserów będzie mógł być przystrojony jakimś dodatkiem jak bita śmietana czy wisionka.
Najpierw budujemy interfejs Deser:
1 2 3 4 5 6 |
public interface Deser { public String getName(); } |
Teraz tworzymy jakis deser np. Lody Waniliowe:
1 2 3 4 5 6 7 8 9 |
public class LodyWaniliowe implements Deser{ public String getName() { return "Lody waniliowe"; } } |
Dekoratory powinny implementować ten sam interfejs co klasa oryginalna. Stwórzmy więc abstrakcyjna klasę dla wszystkich dekoratorów:
1 2 3 4 5 6 7 8 9 |
abstract public class DeserDekorator implements Deser { protected Deser deser; protected DeserDekorator(Deser deser) { this.deser = deser; } } |
Teraz tworzymy dwa prawdziwe dekoratory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class BitaSmietana extends DeserDekorator{ protected BitaSmietana(Deser deser) { super(deser); } public String getName() { return deser.getName() + " plus bita śmietana"; } } public class Wisionka extends DeserDekorator { protected Wisionka(Deser deser) { super(deser); } public String getName() { return deser.getName() + " i wisionka"; } } |
Metody getName() dekoratorów najpierw delegują metodę do obiektu wyżej i dodają nową funkcjonalność w postaci nazwy dekoratora.
Sprawdźmy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Kawiarenka { public static void main(String[] args) { Deser deser = new LodyWaniliowe(); deser = new BitaSmietana(deser); deser = new BitaSmietana(deser); deser = new Wisionka(deser); System.out.println(deser.getName()); } } |
Wynik działania programu:
Lody waniliowe plus bita śmietana plus bita śmietana i wisionka
Diagram wzorca: