Здесь показаны различия между двумя версиями данной страницы.
| Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
|
php:shablony_proektirovanija:structural_patterns:decorator [2023/08/17 13:56] werwolf |
php:shablony_proektirovanija:structural_patterns:decorator [2023/08/17 13:58] (текущий) werwolf |
||
|---|---|---|---|
| Строка 37: | Строка 37: | ||
| - | <code><summary><strong>Связи с другими паттернами</strong></summary> | + | **Связи с другими паттернами** |
| - | <br> | + | |
| - | Адаптер меняет интерфейс существующего объекта. Декоратор улучшает другой объект без изменения его интерфейса. Причём | + | |
| - | Декоратор поддерживает рекурсивную вложенность, чего не скажешь об Адаптере.<br> | + | |
| - | <br> | + | |
| - | Адаптер предоставляет классу альтернативный интерфейс. Декоратор предоставляет расширенный интерфейс. Заместитель | + | |
| - | предоставляет тот же интерфейс.<br> | + | |
| - | <br> | + | |
| - | Цепочка обязанностей и Декоратор имеют очень похожие структуры. Оба паттерна базируются на принципе рекурсивного | + | |
| - | выполнения операции через серию связанных объектов. Но есть и несколько важных отличий.<br> | + | |
| - | <br> | + | |
| - | Обработчики в Цепочке обязанностей могут выполнять произвольные действия, независимые друг от друга, а также в любой | + | |
| - | момент прерывать дальнейшую передачу по цепочке. С другой стороны Декораторы расширяют какое-то определённое действие, | + | |
| - | не ломая интерфейс базовой операции и не прерывая выполнение остальных декораторов.<br> | + | |
| - | <br> | + | |
| - | Компоновщик и Декоратор имеют похожие структуры классов из-за того, что оба построены на рекурсивной вложенности. Она | + | |
| - | позволяет связать в одну структуру бесконечное количество объектов.<br> | + | |
| - | <br> | + | |
| - | Декоратор оборачивает только один объект, а узел Компоновщика может иметь много детей. Декоратор добавляет вложенному | + | |
| - | объекту новую функциональность, а Компоновщик не добавляет ничего нового, но «суммирует» результаты всех своих детей.<br> | + | |
| - | <br> | + | |
| - | Но они могут и сотрудничать: Компоновщик может использовать Декоратор, чтобы переопределить функции отдельных частей | + | |
| - | дерева компонентов.<br> | + | |
| - | <br> | + | |
| - | Архитектура, построенная на Компоновщиках и Декораторах, часто может быть улучшена за счёт внедрения Прототипа. Он | + | |
| - | позволяет клонировать сложные структуры объектов, а не собирать их заново.<br> | + | |
| - | <br> | + | |
| - | Стратегия меняет поведение объекта «изнутри», а Декоратор изменяет его «снаружи».<br> | + | |
| - | <br> | + | |
| - | Декоратор и Заместитель имеют схожие структуры, но разные назначения. Они похожи тем, что оба построены на принципе | + | |
| - | композиции и делегируют работу другим объектам. Паттерны отличаются тем, что Заместитель сам управляет жизнью сервисного | + | |
| - | объекта, а обёртывание Декораторов контролируется клиентом.<br></code> | + | |
| + | Адаптер меняет интерфейс существующего объекта. Декоратор улучшает другой объект без изменения его интерфейса. Причём\\ | ||
| + | Декоратор поддерживает рекурсивную вложенность, чего не скажешь об Адаптере. | ||
| + | |||
| + | Адаптер предоставляет классу альтернативный интерфейс. Декоратор предоставляет расширенный интерфейс. Заместитель\\ | ||
| + | предоставляет тот же интерфейс. | ||
| + | |||
| + | Цепочка обязанностей и Декоратор имеют очень похожие структуры. Оба паттерна базируются на принципе рекурсивного\\ | ||
| + | выполнения операции через серию связанных объектов. Но есть и несколько важных отличий. | ||
| + | |||
| + | Обработчики в Цепочке обязанностей могут выполнять произвольные действия, независимые друг от друга, а также в любой\\ | ||
| + | момент прерывать дальнейшую передачу по цепочке. С другой стороны Декораторы расширяют какое-то определённое действие,\\ | ||
| + | не ломая интерфейс базовой операции и не прерывая выполнение остальных декораторов. | ||
| + | |||
| + | Компоновщик и Декоратор имеют похожие структуры классов из-за того, что оба построены на рекурсивной вложенности. Она\\ | ||
| + | позволяет связать в одну структуру бесконечное количество объектов. | ||
| + | |||
| + | Декоратор оборачивает только один объект, а узел Компоновщика может иметь много детей. Декоратор добавляет вложенному\\ | ||
| + | объекту новую функциональность, а Компоновщик не добавляет ничего нового, но «суммирует» результаты всех своих детей. | ||
| + | |||
| + | Но они могут и сотрудничать: Компоновщик может использовать Декоратор, чтобы переопределить функции отдельных частей\\ | ||
| + | дерева компонентов. | ||
| + | |||
| + | Архитектура, построенная на Компоновщиках и Декораторах, часто может быть улучшена за счёт внедрения Прототипа. Он\\ | ||
| + | позволяет клонировать сложные структуры объектов, а не собирать их заново. | ||
| + | |||
| + | Стратегия меняет поведение объекта «изнутри», а Декоратор изменяет его «снаружи». | ||
| + | |||
| + | Декоратор и Заместитель имеют схожие структуры, но разные назначения. Они похожи тем, что оба построены на принципе\\ | ||
| + | композиции и делегируют работу другим объектам. Паттерны отличаются тем, что Заместитель сам управляет жизнью сервисного\\ | ||
| + | объекта, а обёртывание Декораторов контролируется клиентом. | ||
| + | |||
| + | |||
| + | <code php> | ||
| + | |||
| + | <?php | ||
| + | |||
| + | interface Worker | ||
| + | { | ||
| + | public function countSalary(): int; | ||
| + | } | ||
| + | |||
| + | abstract class WorkerDecorator implements Worker | ||
| + | { | ||
| + | public Worker $worker; | ||
| + | |||
| + | public function __construct(Worker $worker) | ||
| + | { | ||
| + | $this->worker = $worker; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class Developer implements Worker | ||
| + | { | ||
| + | public function countSalary(): int | ||
| + | { | ||
| + | return 20 * 3000; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class DeveloperOverTime extends WorkerDecorator | ||
| + | { | ||
| + | public function countSalary(): int | ||
| + | { | ||
| + | return $this->worker->countSalary() + $this->worker->countSalary() * 0.2; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | $developer = new Developer(); | ||
| + | $developerOverTime = new DeveloperOverTime($developer); | ||
| + | |||
| + | echo $developer->countSalary(); // 60000 | ||
| + | echo $developerOverTime->countSalary(); // 72000 | ||
| + | |||
| + | // ----------- 2 ----------- | ||
| + | |||
| + | interface Coffee | ||
| + | { | ||
| + | public function getCost(); | ||
| + | |||
| + | public function getDescription(); | ||
| + | } | ||
| + | |||
| + | class SimpleCoffee implements Coffee | ||
| + | { | ||
| + | public function getCost() | ||
| + | { | ||
| + | return 10; | ||
| + | } | ||
| + | |||
| + | public function getDescription() | ||
| + | { | ||
| + | return 'Simple coffee'; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class MilkCoffee implements Coffee | ||
| + | { | ||
| + | protected $coffee; | ||
| + | |||
| + | public function __construct(Coffee $coffee) | ||
| + | { | ||
| + | $this->coffee = $coffee; | ||
| + | } | ||
| + | |||
| + | public function getCost() | ||
| + | { | ||
| + | return $this->coffee->getCost() + 2; | ||
| + | } | ||
| + | |||
| + | public function getDescription() | ||
| + | { | ||
| + | return $this->coffee->getDescription() . ', milk'; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class WhipCoffee implements Coffee | ||
| + | { | ||
| + | protected $coffee; | ||
| + | |||
| + | public function __construct(Coffee $coffee) | ||
| + | { | ||
| + | $this->coffee = $coffee; | ||
| + | } | ||
| + | |||
| + | public function getCost() | ||
| + | { | ||
| + | return $this->coffee->getCost() + 5; | ||
| + | } | ||
| + | |||
| + | public function getDescription() | ||
| + | { | ||
| + | return $this->coffee->getDescription() . ', whip'; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | $someCoffee = new SimpleCoffee(); | ||
| + | echo $someCoffee->getCost(); // 10 | ||
| + | echo $someCoffee->getDescription(); // Simple Coffee | ||
| + | |||
| + | $someCoffee = new MilkCoffee($someCoffee); | ||
| + | echo $someCoffee->getCost(); // 12 | ||
| + | echo $someCoffee->getDescription(); // Simple Coffee, milk | ||
| + | |||
| + | $someCoffee = new WhipCoffee($someCoffee); | ||
| + | echo $someCoffee->getCost(); // 17 | ||
| + | echo $someCoffee->getDescription(); // Simple Coffee, milk, whip | ||
| + | |||
| + | |||
| + | // ----------- 3 ----------- | ||
| + | |||
| + | interface Booking | ||
| + | { | ||
| + | public function calculatePrice(): int; | ||
| + | |||
| + | public function getDescription(): string; | ||
| + | } | ||
| + | |||
| + | // Шаблон "Декоратор" хранит ссылку на объект (компонент), определяет его интерфейс и переадресует на него рабочие запросы. | ||
| + | abstract class BookingDecorator implements Booking | ||
| + | { | ||
| + | public function __construct(protected Booking $booking) | ||
| + | { | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Системный компонент определяет интерфейс объекта, на который могут быть динамически возложены дополнительные обязанности | ||
| + | class DoubleRoomBooking implements Booking | ||
| + | { | ||
| + | public function calculatePrice(): int | ||
| + | { | ||
| + | return 40; | ||
| + | } | ||
| + | |||
| + | public function getDescription(): string | ||
| + | { | ||
| + | return 'double room'; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Конкретный компонент определяет объект, на который возлагаются дополнительные обязанности. | ||
| + | class ExtraBed extends BookingDecorator | ||
| + | { | ||
| + | private const PRICE = 30; | ||
| + | |||
| + | public function calculatePrice(): int | ||
| + | { | ||
| + | return $this->booking->calculatePrice() + self::PRICE; | ||
| + | } | ||
| + | |||
| + | public function getDescription(): string | ||
| + | { | ||
| + | return $this->booking->getDescription() . ' with extra bed'; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class WiFi extends BookingDecorator | ||
| + | { | ||
| + | private const PRICE = 2; | ||
| + | |||
| + | public function calculatePrice(): int | ||
| + | { | ||
| + | return $this->booking->calculatePrice() + self::PRICE; | ||
| + | } | ||
| + | |||
| + | public function getDescription(): string | ||
| + | { | ||
| + | return $this->booking->getDescription() . ' with wifi'; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | $booking = new DoubleRoomBooking(); | ||
| + | |||
| + | echo $booking->calculatePrice(); // 4 | ||
| + | echo $booking->getDescription(); // double room | ||
| + | |||
| + | $booking2 = new DoubleRoomBooking(); | ||
| + | $booking2 = new WiFi($booking2); | ||
| + | |||
| + | echo $booking2->calculatePrice(); // 42 | ||
| + | echo $booking2->getDescription(); // double room with wifi | ||
| + | |||
| + | $booking3 = new DoubleRoomBooking(); | ||
| + | $booking3 = new WiFi($booking); | ||
| + | $booking3 = new ExtraBed($booking); | ||
| + | |||
| + | echo $booking3->calculatePrice(); // 72 | ||
| + | echo $booking3->getDescription(); // double room with wifi with extra bed | ||
| + | </code> | ||