Dekorator wzorzec strukturalny umożliwiający dodawanie obiektom nowych zachowań w sposób dynamiczny.
Problem:
Stwórzmy aplikację dla baru kanapkowego. Zobaczmy jak mogłaby ona wyglądać bez implementacji wzorca projektowego.

class Sandwitch:
package sandwiches; public class Sandwich { private String description; private int cost; public Sandwich(String description, int cost) { this.description = description; this.cost = cost; } public String getDescription() { return description; } public int getCost() { return cost; } }
class SandwichWithButter:
package sandwiches; public class SandwichWitchButter extends Sandwich { public SandwichWitchButter() { super("Kanapka z masłem", 3); } }
class SandwichWithGuacamole:
package sandwiches; public class SandwichWitchGuacamole extends Sandwich{ public SandwichWitchGuacamole() { super("Kanapka z Guakamole", 7); } }
class SandwichWithCheese:
package sandwiches; public class SandwichWithCheese extends Sandwich{ public SandwichWithCheese() { super("Kanapka z serem", 6); } }
class SandwichWithCheeseAndHam:
package sandwiches; public class SandwichWithCheeseAndHam extends Sandwich{ public SandwichWithCheeseAndHam() { super("Kanapka z serem i szynką", 10); } }
class SandwichWithHam:
package sandwiches; public class SandwichWithHam extends Sandwich{ public SandwichWithHam() { super("Kanapka z szynką", 6); } }
class Main:
import sandwiches.Sandwich; import sandwiches.SandwichWitchButter; import sandwiches.SandwichWithCheese; import sandwiches.SandwichWithCheeseAndHam; public class Main { public static void main(String[] args) { Sandwich butter = new SandwichWitchButter(); Sandwich cheese = new SandwichWithCheese(); Sandwich cheeseHam = new SandwichWithCheeseAndHam(); } }
No i świetnie, po co nam w ogóle jakiś wzorzec, skoro wszystko działa jak należy? No właśnie, a co jeśli chcielibyśmy rozbudować naszą aplikację i dodać do niej kanapkę z masłem, serem, szynką i guacamole, a potem kolejną z guacamole i szynką i jeszcze kolejną z dowolną konfiguracją składników? Robi się problem, ponieważ jesteśmy zmuszeni tworzyć kolejne klasy lub je modyfikować a nasza aplikacja powinna być otwarta na rozbudowę a zamknięta na modyfikacje. Dlatego z pomocą przychodzi nam wzorzec projektowy Dekorator.
Rozwiązanie:

class Sandwich:
package sandwiches; public abstract class Sandwich { private String description; private int cost; public Sandwich(String description, int cost) { this.description = description; this.cost = cost; } public String getDescription() { return description; } public int getCost() { return cost; } }
class Bread:
package sandwiches; public class Bread extends Sandwich{ public Bread() { super("Kanapka", 2); } }
class Butter:
package sandwiches; public class Butter extends SandwichDecorator{ public Butter(Sandwich sandwich) { super(sandwich); } @Override public int getCost() { return sandwich.getCost() + 1; } }
class Cheese:
package sandwiches; public class Cheese extends SandwichDecorator{ public Cheese(Sandwich sandwich) { super(sandwich); } @Override public int getCost() { return sandwich.getCost() + 6; } }
class Cucumber:
package sandwiches; public class Cucumber extends SandwichDecorator{ public Cucumber(Sandwich sandwich) { super(sandwich); } @Override public int getCost() { return sandwich.getCost() + 2; } }
class Guacamole:
package sandwiches; public class Guacamole extends SandwichDecorator{ public Guacamole(Sandwich sandwich) { super(sandwich); } @Override public int getCost() { return sandwich.getCost() + 4; } }
class Ham:
package sandwiches; public class Ham extends SandwichDecorator{ public Ham(Sandwich sandwich) { super(sandwich); } @Override public int getCost() { return sandwich.getCost() + 3; } }
class SandwichDecorator:
package sandwiches; public abstract class SandwichDecorator extends Sandwich{ protected Sandwich sandwich; public SandwichDecorator(Sandwich sandwich) { super("", 0); this.sandwich = sandwich; } }
class Main:
import sandwiches.*; public class Main { public static void main(String[] args) { Sandwich sandwichExtra = new Butter(new Ham(new Cheese(new Cucumber(new Bread())))); Sandwich sandwichFit = new Butter(new Cucumber(new Bread())); } }
Zauważ co właśnie zrobiliśmy, rozbiliśmy gotowe kanapki na poszczególne składniki (dekoratory). Nasza klasa „Bread” robi za podstawę kanapki dlatego dziedziczy klasę „Sandwich”, natomiast klasy „Butter”, „Cheese”, „Ham”, „Cucumber”, „Guacamole” dziedziczą po klasie „SandwichDecorator” i służą jako składniki. W klasach dekoracyjnych nadpisujemy metodę „getCost” dodając cenę naszego składnika. Klasa „SandwichDecorator” w konstruktorze przyjmuję jako parametr obiekt typu „Sandwich” co jak można zauważyć w metodzie „main”, umożliwia nam potem tworzenie takich „owiniętych” obiektów. Wywołanie metody „getCost()” na gotowej kanapce zwróci nam koszt kanapki ze składnikami.
Mam nadzieję, że dzięki temu artykułowi chociaż trochę zrozumiałeś działanie wzorca Dekorator.