Паттерн Decorator (Wrapper) в php
Паттерн «Decorator» («Wrapper») динамически добавляет объекту новые обязанности.
Является гибкой альтернативой подклассам, расширяющим базовый класс.
Схема следующая:
Поместить целевой объект в другой объект, называемый декоратором, который большинство обращений переводит к переданному объекту, а часть (особенности) реализует самостоятельно.
Могут вкладываться друг в друга по цепочке.
Интерфейс декоратора и декорируемого объекта должны совпадать.
Отличается от паттерна «Strategy» тем, что в последнем всё наоборот — главным является целевой объект, который распределяет обращения «расширениям».
Листинг1. Decorator.php (PHP 5.2.5)
1 2 3 4 5 6 7 8 9 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<? //интерфейс, который будут реализовывать все: и целевой класс, и все декораторы interface ClassInterface { public function method1(); public function method2(); } //целевой класс class TargetClass implements ClassInterface { public function method1() { echo 'called "method1"<br />'; } public function method2() { echo '"called method2"<br />'; } } //от этого класса будут наследоваться все декораторы abstract class AbstractTargetDecorator implements ClassInterface { private $_componentObject = null; //т.к. реализуем интерфейс public function method1() { $this->_componentObject->method1(); } public function method2() { $this->_componentObject->method2(); } public function __construct(ClassInterface $componentObject) { $this->_componentObject = $componentObject; } //для вызова методов декораторов по цепочке public function __call($methodName, $arguments) { //чтобы не было ошибок; можно кидать Exception if (method_exists($this->_componentObject, $methodName)) call_user_func_array(array($this->_componentObject, $methodName), $arguments); } public function abstractTargetDecoratorMethod() { echo 'called "abstractTargetDecoratorMethod"<br />'; } } //первый декоратор class TargetDecorator1 extends AbstractTargetDecorator { public function decorator1Method() { echo 'called "decorator1Method"<br />'; } } //второй декоратор class TargetDecorator2 extends AbstractTargetDecorator { public function decorator2Method() { echo 'called "decorator2Method"<br />'; } } //Использование $decoratedObject = new TargetDecorator2(new TargetDecorator1(new TargetClass())); //$decoratedObject = new TargetDecorator1(new TargetDecorator2(new TargetClass())); //$decoratedObject = new TargetDecorator2(new TargetClass()); $decoratedObject->decorator1Method(); $decoratedObject->decorator2Method(); $decoratedObject->method1(); $decoratedObject->method2(); $decoratedObject->abstractTargetDecoratorMethod(); ?> |
Ну, вообще-то, что совсем необязательно AbstractTargetDecorator‘у реализовывать интерфейс ClassInterface. Это сделано для того, чтобы потомки могли друг в друга вкладываться. Хотя это и не нужно для паттерна декоратор.
Можно применить в следующем примере:
Есть 2 типа статей, которые состоят из нескольких типов модулей каждый. Причём эти модули одинаковы для обоих типов статей за исключением механизма их хранения. При этом все они наследуются от класса module.
Специфичность ситуации в том, что данные сохраняются в виде сериализованных объектов модулей, и второй тип статей вводился намного позже первого (накопилось много сохранённых данных).
При этом наследовать каждый модуль второго типа статей от соответствующего модуля первого типа нехорошо, т.к. механизм хранения один для всех модулей одного типа.
Именно в этом случае мог бы помочь паттерн Декоратор: для каждого типа статей создаётся класс, отвечающий за сохранение в зависимости от типа статьи, и декорирующий исходный объект.
Similar Posts
LEAVE A COMMENT
Для отправки комментария вам необходимо авторизоваться.