Wzorzec Dekorator jest jednym z podstawowych wzorców projektowych. Stosując wzorzec Dekorator możemy dodawać danemu obiektowi nowe zachowania. Wzorzec Dekorator stanowi doskonałą alternatywę dla dziedziczenia. Pracę ze wzorcem zaczynamy od utworzenie składnika podstawowego, do którego będziemy dodawać nowe zachowania tzw. dekoratory.
Każdy z obiektów podstawowych może mieć dowolną ilość dekoratorów. Należy pamiętać, żeby obiekty dekorujące były tego samego typu co dekorowane. Obiekty mogą być dekorowane w dowolnym momencie działania programu.

Spróbujmy zastosować wzorzec Dekorator na przykładzie. Załóżmy, że prowadzimy bar z kanapkami, który ma cztery stałe kanapki w ofercie: z szynką, serem, kurczakiem i wege. Do każdej z tych kanapek możemy dodać dodatkowe składniki takie jak np: jajko, pomidor, ogórek, cebula, sos itd. Im więcej składników, tym więcej różnych wariantów kanapek możemy uzyskać. Bez zastosowania wzorca Dekorator trzeba by stworzyć oddzielną klasą z każdym wariantem dziedziczącym po klasie abstrakcyjnej „Kanapka”. Takie rozwiązania prowadzi do szybkiej „eksplozji klas”.

Po zastosowaniu wzorca Dekorator diagram UML wyglądałby następująco:

Kod z użyciem wzorca Dekorator jest jasny i przejrzysty. Z łatwością możemy dodać nowe dodatki, bez ingerencji w istniejący już kod oraz dowolnie dekorować podstawowe wersje kanapek dodatkowymi elementami. Stwórzmy kanapkę z kurczakiem, pomidorem i jajkiem i obliczmy jej koszt.
public abstract class Kanapka {
String opis = "Kanapka nieznana";
public String pobierzOpis(){
return opis;
}
public abstract double obliczCene();
}
public class KanapkaZKurczakiem extends Kanapka{
public KanapkaZKurczakiem(){
opis = "Kanapka z kurczakiem";
}
@Override
public double obliczCene() {
return 7.50;
}
}
public abstract class SkladnikDekorator extends Kanapka{
public abstract String pobierzOpis();
}
public class Pomidor extends SkladnikDekorator{
Kanapka kanapka;
public Pomidor(Kanapka kanapka){
this.kanapka = kanapka;
}
@Override
public double obliczCene() {
return kanapka.obliczCene() + 0.70;
}
@Override
public String pobierzOpis() {
return kanapka.pobierzOpis() + " + pomidor";
}
}
public class Jajko extends SkladnikDekorator{
Kanapka kanapka;
public Jajko(Kanapka kanapka){
this.kanapka = kanapka;
}
@Override
public double obliczCene() {
return kanapka.obliczCene() + 1.50;
}
@Override
public String pobierzOpis() {
return kanapka.pobierzOpis() + " + jajko";
}
}
public class Main {
public static void main(String[] args) {
Kanapka kanapka1 = new KanapkaZKurczakiem();
kanapka1 = new Jajko(kanapka1);
kanapka1 = new Pomidor(kanapka1);
System.out.println(kanapka1.pobierzOpis() + ", koszt kanapki: " + kanapka1.obliczCene());
}
}
Wykonując powyższy kod dostaniemy w odpowiedzi: Kanapka z kurczakiem + jajko + pomidor, koszt kanapki: 9.7.
