Шаблоны проектирования. Composite.

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

Итак, шаблон Composite позволяет нам организовывать объекты в группы, причем работа с отдельными объектами и группами ничем друг от друга не отличается (или почти ничем). То есть мы можем обращаться с общим так же как и с частным, при этом не теряя тот факт, что общее состоит из частного…

Немного запутанно, но давайте постепенно разберем этот шаблон на примере. Допустим мы хотим создать структуру для нашего электронного кулинара. У нас есть ингредиенты: яйца, молоко, крупа, масло, фарш, помидоры, лук, мука и т.д. У нас также есть и производимая продукция: пицца, салат, каша. Взяв, например, крупу, масло и молоко, можно приготовить кашу. А из фарша, муки, помидоров, яиц и лука получится пицца. У каждого продукта и ингредиента есть пищевая ценность. Можно накидать небольшой набор классов для ингредиентов:

 

В принципе все логично. Но как быть с объектами-контейнерами? В разные продукты входят разные ингредиенты и нужно четко учитывать что и куда добавлено. К тому же неплохо было бы организовать удаление из ингредиентов определенного наименования (ну, например, продукты заготовили, а потом передумали делать блюдо).

Здесь и выходит на сцену шаблон Composite. Его основная идея – в общем интерфейсе как и общего, так и частного. Именно тогда мы сможем стереть грань между продуктами и ингредиентами и работать с ними как с общим целым, так и с частным. Перепишем нашу структуру так:

 

Идея понятна. Все классы в нашей системе будут расширять реализовывать методы абстрактного Prod. Это обеспечит единство интерфейса и гарантию того что мы работаем с объектами типа Prod. Сейчас мы может добавлять в класс пиццы необходимые ингредиенты или убирать их из нашего рецепта, вычислять пищевую ценность. Но как быть с реализацией классов для ингредиентов? Ведь им не нужны методы добавления и удаления продуктов…

Для решения этой задачи можно реализовать эти методы так:

 

В таком случае мы должны реализовать класс ProdException как расширение класса Exception. Это не сложно, но все таки есть некий дискомфорт.

Другой варианты выйти из этой ситуации – это создать один дополнительный абстрактный класс контейнеров (пицца, каша, салат). Тогда можно будет перенести методы относящиеся к контейнерам туда, а в Prod останется только вычисление пищевой ценности. Сделаем так:

 

Таким образом мы избавились от необходимости реализации лишних методов для ингредиентов и сохранили с другой стороны единство типа. Единственное неудобство состоит в методе getContainer. Для ингредиента он всегда возвращает null, так как ингредиент не может содержать в себе что-либо. Для продукта он возвращает этот продукт как контейнер содержащий ингредиенты. Это необходимо для клиентского кода, когда используется общий тип Prod, невозможно определить имеем мы дело с контейнером или с ингредиентом.

Таким образом, мы рассмотрели отличный шаблон для работы с объектами и группами объектов. Однако, стоит помнить, что применение этого объекта оптимально только в тех местах, где исходные объекты системы в большой степени взаимозаменяемы и лишены больших отличий между собой. Иначе нам придется наполнять наш код лишними проверками особенностей. Кроме того, глядя на то, как мы вычисляем пищевую ценность, очевидно, что для сложного продукта, который состоит из многих продуктов и ингредиентов, вызов метода подсчета вызывает лавинообразный поток вычислений. В нашем случае это не страшно. Методы достаточно просты. Но вот если бы метод call обращался к базе данных и делал сложный запрос, то тут бы у нас возникли проблемы. Важно оценить все особенности вашей задачи и решить насколько применение этого шаблона оправдано…

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">