Статьи

Шаблоны проектирования: шаблон декоратора

Ранее в этой серии мы исследовали шаблоны дизайна фасадов и адаптеров в этой серии. Используя фасад, мы можем упростить большие системы, а внедряя адаптер, мы можем оставаться в безопасности при работе с внешним API и классами. Теперь мы рассмотрим шаблон дизайна декоратора, который также относится к категории структурных шаблонов.

Мы можем использовать шаблон декоратора, когда просто хотим дать некоторую дополнительную ответственность нашему базовому классу. Этот шаблон проектирования является отличной альтернативой функции подкласса для расширения функциональности с некоторыми дополнительными преимуществами.

Если вы смущены и думаете, что мы можем достичь той же функциональности с помощью функции подкласса, то позвольте мне показать вам несколько примеров кода, которые устранят вашу путаницу и заставят вас полюбить шаблон декоратора.

Я собираюсь взять пример класса, который отвечает за создание контента для электронной почты. В следующем блоке кода, как вы можете видеть, этот класс очень хорошо работает для генерации содержимого электронной почты без каких-либо изменений.

1
2
3
4
5
6
7
8
9
class eMailBody {
    private $header = ‘This is email header’;
    private $footer = ‘This is email Footer’;
    public $body = »;
 
    public function loadBody() {
        $this->body .= «This is Main Email body.<br />»;
    }
}

Мы знаем, что приближается Рождество, и скажем, я хочу поприветствовать моего читателя сообщением для моего следующего электронного письма. Таким образом, я должен добавить сообщение в своем теле письма с изображением, которое выглядит хорошо.

Для этого я могу напрямую редактировать в своем классе электронной почты, что я действительно не хочу делать. Так что я могу реализовать наследование для достижения того же эффекта. Я создаю отдельный дочерний класс основного класса тела письма:

01
02
03
04
05
06
07
08
09
10
class christmasEmail extends eMailBody {
    public function loadBody() {
        parent::loadBody();
        $this->body .= «Added Content for Xmas<br />»;
    }
}
 
$christmasEmail = new christmasEmail();
$christmasEmail->loadBody();
echo $christmasEmail->body;

Итак, я закончил с моим кодом, и через несколько дней я хочу отправить электронное письмо с новогодними поздравлениями. Мы можем использовать тот же метод, что и на Рождество.

01
02
03
04
05
06
07
08
09
10
class newYearEmail extends eMailBody {
    public function loadBody() {
        parent::loadBody();
        $this->body .= «Added Content for New Year<br />»;
    }
}
 
$newYearEmail = new newYearEmail();
$newYearEmail->loadBody();
echo $newYearEmail->body;

Все прошло без проблем. Допустим, я забыл приветствовать своих посетителей в обоих случаях (Рождество и Новый год) и хочу отправить оба приветствия в одном письме без изменения кода в базовом классе.

Ваш ум сразу же наполняется следующим вопросом: помогут ли здесь подклассы и наследование? Я бы за такой путь, но для этого нам понадобится дополнительный / ненужный код. Мы можем использовать признаки, которые позволяют нам реализовать нечто похожее на множественное наследование.

Проблема, которую мы обсудили в предыдущем разделе, может быть решена путем реализации шаблона декоратора.

Согласно Википедии :

Шаблон декоратора (также известный как Wrapper, альтернативное именование, совместно используемое с шаблоном Adapter) — это шаблон проектирования, который позволяет добавлять поведение к отдельному объекту, статически или динамически, без влияния на поведение других объектов того же класса.

В приведенном выше разделе мы видели, что мы можем расширять функции / поведение, используя один подкласс, но когда дело доходит до добавления нескольких функций / моделей, это становится длинным и сложным. И вот где мы должны использовать шаблон декоратора.

1
2
3
interface eMailBody {
    public function loadBody();
}

Это простой интерфейс, позволяющий убедиться, что некоторый класс должен реализовывать необходимые методы.

1
2
3
4
5
class eMail implements eMailBody {
    public function loadBody() {
        echo «This is Main Email body.<br />»;
    }
}

Это основной класс, который генерирует тело письма по умолчанию, которое я обычно использую для отправки писем. Что мне нужно, однако, это изменить содержание тела на основе некоторого случая, но без изменения основного класса электронной почты.

01
02
03
04
05
06
07
08
09
10
11
abstract class emailBodyDecorator implements eMailBody {
         
    protected $emailBody;
     
    public function __construct(eMailBody $emailBody) {
        $this->emailBody = $emailBody;
    }
     
    abstract public function loadBody();
     
}

Это наш основной класс декоратора, который содержит ссылку на наш основной класс электронной почты и изменяет его поведение по мере необходимости. Здесь мы определили один абстрактный метод loadBody , который необходимо реализовать для изменения поведения подчиненного декоратора.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
class christmasEmailBody extends emailBodyDecorator {
         
    public function loadBody() {
         
        echo ‘This is Extra Content for Christmas<br />’;
        $this->emailBody->loadBody();
         
    }
     
}
 
class newYearEmailBody extends emailBodyDecorator {
 
    public function loadBody() {
         
        echo ‘This is Extra Content for New Year.<br />’;
        $this->emailBody->loadBody();
         
    }
 
}

Здесь мы создали два подкласса главного декоратора, который фактически выполняет изменение поведения для нашего основного класса электронной почты.

Мы создали все необходимые элементы. Все, что нам нужно сделать, это использовать наш код и наслаждаться.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
interface eMailBody {
    public function loadBody();
}
 
class eMail implements eMailBody {
    public function loadBody() {
        echo «This is Main Email body.<br />»;
    }
}
 
abstract class emailBodyDecorator implements eMailBody {
     
    protected $emailBody;
     
    public function __construct(eMailBody $emailBody) {
        $this->emailBody = $emailBody;
    }
     
    abstract public function loadBody();
     
}
 
class christmasEmailBody extends emailBodyDecorator {
     
    public function loadBody() {
         
        echo ‘This is Extra Content for Christmas<br />’;
        $this->emailBody->loadBody();
         
    }
     
}
 
class newYearEmailBody extends emailBodyDecorator {
 
    public function loadBody() {
         
        echo ‘This is Extra Content for New Year.<br />’;
        $this->emailBody->loadBody();
         
    }
 
}

Теперь мы будем использовать этот класс декораторов по мере необходимости:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
 * Normal Email
 */
 
$email = new eMail();
$email->loadBody();
 
// Output
This is Main Email body.
 
 
/*
 * Email with Xmas Greetings
 */
 
$email = new eMail();
$email = new christmasEmailBody($email);
$email->loadBody();
 
// Output
This is Extra Content for Christmas
This is Main Email body.
 
/*
 * Email with New Year Greetings
 */
 
$email = new eMail();
$email = new newYearEmailBody($email);
$email->loadBody();
 
 
// Output
This is Extra Content for New Year.
This is Main Email body.
 
/*
 * Email with Xmas and New Year Greetings
 */
 
$email = new eMail();
$email = new christmasEmailBody($email);
$email = new newYearEmailBody($email);
$email->loadBody();
 
// Output
This is Extra Content for New Year.
This is Extra Content for Christmas
This is Main Email body.

Мы видим, что теперь мы изменили тело письма без изменения основного класса письма.

Каждое имеющееся у нас приложение требует каких-то изменений и / или улучшений через равные промежутки времени. Таким образом, в таком случае мы можем реализовать шаблон проектирования декоратора, и это в конечном итоге улучшит качество кода и сделает наш код более расширяемым.

Это была моя попытка объяснить вам шаблон декоратора, но если у вас есть дополнительные комментарии или вопросы, пожалуйста, не стесняйтесь добавлять их в канал ниже.