Декораторът е представител на структурните шаблони. Неговото предназначение е динамично да добавя характеристики и функционалности към вече съществуващи обекти, без това да променя тяхната структура. Декораторът се явява като обгръщащ (wrapper) клас за вече съществуващия и имплементира същия интерфейс.
Като илюстрация на шаблона декоратор, нека разгледме следния пример:
Декларираме интерфейс Book:
public interface Book {
String decorateBook();
}
Необходим е също така клас BookImpl:
public class BookImpl implements Book{
@Override
public String decorateBook() {
return "Book has: ";
}
}
Следващата стъпка е създаването на абстрактен клас BookDecorator, в който като частно поле декларираме обект от интерфейса:
public abstract class BookDecorator implements Book {
private Book book;
public BookDecorator(Book book) {
this.book = book;
}
@Override
public String decorateBook() {
return book.decorateBook();
}
}
Този клас ще бъде базовия декоратор, като всички останали ще го наследяват.
public class HardCoverDecorator extends BookDecorator {
public HardCoverDecorator(Book book) {
super(book);
}
@Override
public String decorateBook() {
return super.decorateBook() + addHardCover();
}
private String addHardCover() {
return "\n hard cover";
}
}
public class CoverIllustrationsDecorator extends BookDecorator {
public CoverIllustrationsDecorator(Book book){
super(book);
}
@Override
public String decorateBook() {
return super.decorateBook() + addCoverIllustrations();
}
private String addCoverIllustrations() {
return "\ncover illustrations";
}
}
public class BookIllustrationsDecorator extends BookDecorator {
public BookIllustrationsDecorator(Book book) {
super(book);
}
@Override
public String decorateBook() {
return super.decorateBook() + addBookIllustrations();
}
private String addBookIllustrations() {
return "\n book illustrations";
}
}
public class VolumeDecorator extends BookDecorator {
public VolumeDecorator(Book book) {
super(book);
}
@Override
public String decorateBook() {
return super.decorateBook() + addVolume();
}
private String addVolume() {
return "\n more than one volume";
}
}
Създаването на обекти и използването на различните декоратори е илюстрирано със следващия програмен фрагмент:
public class Main {
public static void main(String[] args) {
Book book = new BookImpl();
Book firstBook = new HardCoverDecorator(book);
System.out.println("First " + firstBook.decorateBook() + "\n");
Book secondBook = new HardCoverDecorator(new CoverIllustrationsDecorator(book));
System.out.println("Second " + secondBook.decorateBook() + "\n");
Book thirdBook = new VolumeDecorator(new HardCoverDecorator(new BookIllustrationsDecorator(book)));
System.out.println("Third " + thirdBook.decorateBook() + "\n");
Book fourthBook = new VolumeDecorator(new VolumeDecorator(book));
System.out.println("Fourth " + fourthBook.decorateBook());
}
}
Вижда се, че създаването на книги може да става с различни комбинации от декларираните декоратори, както и повече от едно извикване на един и същи декоратор.
Резултатът е следния:
Кога се използва декоратор:
Когато желаем динамично да добавяме отговорности към обекти без това да оказва влияние върху други такива;
Когато е необходимо да добавим функционалности към обект, които може да променим в бъдеще;
Когато добавянето на наследници/подкласове вече не е практично (станат прекалено много).
Предимства от използването на шаблон Декоратор:
Предоставя по-голяма гъвкавост в сравнение със статичното наследяване;\
Разширява и променя поведението на обекта без използване на подкласове/наследници;
Дава възможност за комбиниране на различни функционалности чрез обгръщане на дадения клас с различни декоратори (последните три обекта в горния пример).
Като недостатък на декоратора може да се посочи факта, че е трудно неговото имплементиране по начин, по който неговото поведение да не зависи от поредността на извикванията.