Паттерны проектирования, впервые опубликованные в книге «Design Patterns — Elements of Reusable Object-Oriented Software» Эрихом Гаммой и его товарищами, можно разделить на три группы: порождающие паттерны проектирования (Creational Patterns), структурные паттерны проектирования классов/объектов (Structural Patterns) и паттерны проектирования поведения классов/объектов (Behavioral Patterns). В этой статье я рассмотрю основные структурные шаблоны.
К числу структурных шаблонов принадлежат следующие шаблоны:
− Адаптер (Adapter);
− Декоратор (Decorator) или Оболочка (Wrapper);
− Заместитель (Proxy) или Суррогат (Surrogate);
− Компоновщик (Composite);
− Мост (Bridge), Описатель (Handle) или Тело (Body);
− Приспособленец (Flyweight);
− Фасад (Facade).
Шаблон «Заместитель» (Proxy) решает проблему, когда необходимо управлять доступом к объекту, так чтобы создавать громоздкие объекты «по требованию».
Решение заключается в следующем:
− создать суррогат громоздкого объекта. «Заместитель» хранит ссылку, которая позволяет заместителю обратиться к реальному субъекту (объект класса «Заместитель» может обращаться к объекту класса субъект, если интерфейсы «РеальногоСубъекта» и «Субъекта» одинаковы);
− Поскольку интерфейс «РеальногоСубъекта» идентичен интерфейсу «Субъекта», так, что «Заместителя» можно подставить вместо «РеальногоСубъекта», контролирует доступ к «Реальному субъекту», может отвечать за создание или удаление «РеальногоСубъекта». «Субъект» определяет общий для «РеальногоСубъекта» и «Заместителя» интерфейс так, что «Заместитель» может быть использован везде, где ожидается «РеальныйСубъект»;
− При необходимости запросы могут быть переадресованы «Заместителем» «РеальномуСубъекту».
Рис. 1. UML-диаграмма паттерна «Proxy»
По большому счету, паттерн «Декоратор» есть несколько заложенных друг в друга «Proxy», то есть «Proxy», который проксирует другой «Proxy» и т. д.
«Заместитель» может иметь и другие обязанности, а именно:
− удаленный «Заместитель» может отвечать за кодирование запроса и его аргументов и отправку закодированного запроса реальному «Субъекту»;
− виртуальный «Заместитель» может кэшировать дополнительную информацию о реальном «Субъекте», чтобы отложить его создание;
− защищающий «Заместитель» может проверять, имеет ли вызывающий объект необходимые для выполнения запроса права.
Это очень полезный шаблон, который рекомендуется, особенно начинающим программистам, постоянно держать в голове. Потому что из него выводятся другие паттерны, в том числе «Декоратор», а так же он служит постоянным напоминанием вызова классов через интерфейсы. Это второй паттерн, который автор рекомендует всем держать в голове, после «Factory method».
Рис. 2. UML-диаграмма шаблона «Компоновщик»
Шаблон «Компоновщик» (Composite) решает следующую проблему:
− как обрабатывать группу или композицию структур объектов одновременно?
Стандартное решение предлагает определить классы для композитных и атомарных объектов таким образом, чтобы они реализовывали один и тот же интерфейс.
Вопрос в данном паттерне ровно один, чтобы выполнялось следующее условие: над каждым элементом дерева можно выполнить операцию. И каждая операция приводила к выполнению той же самой операции над всеми своими наследниками. В этом и заключается вся идея данного шаблона.
Шаблон «Мост» (Bridge) решает следующую проблему:
− требуется отделить абстракцию от реализации так, чтобы и то и другое можно было изменять независимо. При использовании наследования реализация жестко привязывается к абстракции, что затрудняет независимую модификацию.
Стандартное решение заключается в том, чтобы поместить абстракцию и реализацию в отдельные иерархии классов.
Рис. 3. UML-диаграмма шаблона «Bridge»
Для примера, представим, что у нас есть бизнес-объект, у которого есть какие-то наследники. Предположим, что у нас есть класс Report и есть разные виды Report: годовой, месячный, недельный и т. д. Задача: распечатать все эти Report в форматах doc, xml, pdf. Будем делать годовой Report в трех видах и т. д.? Не хочется. Паттерн предлагает создать интерфейс, в нашем примере это будет Format, у которого будут имплементации doc, xml, pdf. Все взаимодействие происходит только через обращение интерфейса Report к Format. Никаких больше стрелочек в UML быть не должно. Поскольку абстракция и реализация, по условию паттерна, должны быть помещены в разные иерархии.
Выглядеть все должно следующим образом: годовой отчет (RefinedAbstraction) должен вызвать у своего родителя (Abstraction), абстрактного класса, у которого описаны методы, например, напечатать заголовок, напечатать таблицу, начать ячейку таблицы, закончить ячейку таблицы и т. д. Все эти методы должны быть описаны у всех типов отчетов (RefinedAbstraction), и каждый должен вызывать метод своего родителя, и не в коем случае не методы Format (Implementor). Далее каждый из методов родителя (Abstraction) должен вызывать абстрактные методы Format (Implementor), а на уровне ConcreteImplementor в зависимости от выбора конкретной имплементации метод будет обрабатываться по-своему: в html, к примеру строка запишется одним способом, в pdf другим.
В этом шаблоне обратная ситуация в «Декоратором». Там код очень сложно понять, сложно составить, но потом, когда код уже составлен, код становится очень простым и понятным. Здесь код очень легко пишется, но так же очень легко портится. Однако шаблон очень полезен, потому что спасает в очень многих ситуациях. Отсюда такая высокая популярность.
В шаблоне «Bridge», как и во всех других шаблонах проектирования, важно понимать, что он не является законченным кодом. Его нужно дорабатывать. Более того, шаблон «Bridge» рекомендуют использовать тогда, когда в проекте две оси изменения. Если в системе три оси изменения, будет более разумно разделить её на несколько систем. Потому что такая система становится «тотально» неподдерживаемой. Разница с шаблоном заключается, по большому счету, в поставленной задаче. Поскольку в шаблоне «Bridge» проблема в том, что у нас есть некоторая абстракция (класс Report, Abstraction) и есть некоторая реализация. И нам нужно с ними что-то делать, потому что они пытаются произвести комбинаторный взрыв, перемножиться друг на друга. Чтобы этого не добиваться, мы их разделили на две половины и, действительно, свели эту задачу к чему-то похожему на абстрактную фабрику. Эти шаблоны взаимодополняющие, «Абстрактная фабрика» — создающий шаблон, «Bridge» — структурный, они работают на разных этапах.
Преимущества «Bridge» заключаются в следующем:
− отделении реализации от интерфейса, то есть, «Реализацию» «Абстракции» можно конфигурировать во время выполнения;
− разделение классов «Абстракция» и «Реализация» устраняет зависимости от реализации, устанавливаемые на этапе компиляции: чтобы изменить класс «Реализация» вовсе не обязательно перекомпилировать код.
Основной недостаток этого шаблона заключается в том, что он очень легко портится.
Литература:
- Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. СПб.: Питер, 2001.
- Ларман К. Применение UML и шаблонов проектирования. Вильямс, 2002.
- DeanLeffingwell, Don Widrig. Managing Software Requirements. Addison-Wesley, 2000.
- Rational Unified Process. Versions 2001–2003. Rational Software Corporation. http://www.rational.com/
- Мартин Фаулер — Архитектура корпоративных программных — М.: «Вильямс», 2007. — С. 544.
- Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — С. 288. —ISBN 0–201–71594–5.
- Эрик Фримен, Элизабет Фримен. Паттерны проектирования = Head First Design Patterns. — СПб: Питер. — 656 с. — ISBN 978–5–459–00435–9.