[Moscow.pm] Strategy ("Стратегия") и "Декоратор"(Decorator/Wrapper) реализация на Perl

Orlovsky Alexander nordicdyno на yandex.ru
Ср Июл 9 10:09:52 PDT 2008


Итак, первыми паттернами для реализации я выбрал родственные шаблоны Strategy ("Стратегия") и "Декоратор"(Decorator/Wrapper).
В начале немного теории. (Не забываем про поисковики :)

Несмотря на то, что "Декоратор" и "Стратегия" в GoF-буке находятся в разных разделах ("Структурные паттерны" и "Паттерны поведения" соответственно), эти паттерны решают похожие задачи. Оба могут удалять условную логику и оба делают это, перемещая такое поведение в один или несколько новых классов вне рамок существующего класса.

Различие заключается в способе использования этих новых классов.

Декораторы (их экземпляры) представляют собой оболочки вокруг объекта (или друг друга), в то время, как один или несколько экземпляров стратегии  используются внутри объекта.

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

Интерфейс стратегии может быть произвольным, в отличие от интерфейса декоратора. Класс декоратора должен реализовывать те же методы, что и классы которые он декорирует (имеет общий с ними интерфейс).

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

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

------------------------------------------------------------------------------------
1) "Декоратор":

Дополнительная информация, а также UML-схема примера реализации:
http://codelab.ru/pattern/decorator/
http://en.wikipedia.org/wiki/Decorator_pattern

Пример кода: http://paste.org/index.php?id=3271

Комментарий к коду:

В коде реализованы два "компонента": SimpleArrayPrinter, PrettyArrayPrinter, наследующих от общего предка ArrayPrinter.
ArrayPrinter служит аналогом абстрактного класса "Component" в схеме из [1]  (см. также ссылки выше).

SimpleArrayPrinter, PrettyArrayPrinter -- это реализации "ConcreteComponent".
SimpleArrayPrinter -- выводит матрицу на экран практически без какого-либо оформления. (не считая разделитель) 
PrettyArrayPrinter -- выводит матрицу с помощью CPAN-модуля Pretty::Table (это сделано для простоты и наглядности, в реальном декораторе, скорее всего содержалась бы больше логики оформления)

Decorator -- аналог абстрактного класса "Decorator" [1], от него наследуются два подкласса декораторов (на схеме это "ConcreteDecorator"-ы)
c именами: VerticalDecorator, ReverseDecorator.
Класс Decorator, в свою очередь, наследует от ArrayPrinter, т.к. должен реализовывать общий с ним интерфейс (чтобы быть прозрачным для 

клиента и других декораторов в цепочке).

VerticalDecorator -- "переворчивает" матрицу на 90%
ReverseDecorator  -- обращает списки элементов в каждой из строк.

На примере можно убедиться, что можно оборачивать любой из объектов семейства ArrayPrinter декораторами в любой последовательности. 

Попробуйте поменять местами вызов декораторов, а также подставить вместо "PrettyArrayPrinter->new(" строку "SimpleArrayPrinter->new(".
Также можно убрать какой-либо декоратор из цепочки (либо убрать все декораторы вовсе), после чего увидеть как измененилось поведение объекта.

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

( см. также Class::Decorator на CPAN )

------------------------------------------------------------------------------------
1) "Стратегия":

 Шаблон "Стратегия" обычно применяют как способ для исключения условной логики.

Дополнительная информация, а также UML-схема примера реализации:
http://codelab.ru/pattern/strategy/
http://en.wikipedia.org/wiki/Strategy_pattern

Пример кода: http://paste.org/index.php?id=3272

Комментарии к коду:
Вообще код достаточно простой и не нуждается в дополнительных комментариях. 

Замечу, что можно реализовать паттерн стратегия и не используя классы стратегий ("ConcreteStartegy").
Вместо ссылки на объект, можно ссылку на функцию, нужным образом обрабатывающую данные. 
Это также будет шаблон "Стратегия". Объекты конкретных стратегий могут понадобится при достаточно сложном внутреннем устройстве реализации стратегии. (к сожалению затрудняюсь привести хороший пример, может быть подписчики помогут?)
---------------------------------------------------------

Литература:
[1] "Приемы объектно-ориентированного проектирования. Паттерны проектирования." Гамма, Хели, Джонсон, Влиссидес
[2] "Рефакторинг с использованием шаблонов" Д. Кириевски
[3] "Рефакторинг" Фаулер


Подробная информация о списке рассылки Moscow-pm