Паттерн Компоновщик
Паттерн Компоновщик (Composite) обеспечивает представление иерархий часть-целое, объединяя объекты в древовидные структуры.
Проблема. Очень часто возникает необходимость создавать маленькие компоненты (примитивы), объединять их в более крупные компоненты (контейнеры), более крупные компоненты соединять в большие компоненты, большие компоненты — в огромные и т. д. При этом клиентам приходится различать компоненты-примитивы и компоненты-контейнеры, работать с ними по-разному. Это усложняет приложение. Паттерн Компоновщик позволяет ликвидировать это различие, его можно применять в следующих случаях:
q необходимо построить иерархию объектов вида часть-целое;
q нужно унифицировать использование как составных, так и индивидуальных объектов.
Решение. Ключевым элементом решения является абстрактный класс Компонент, который является одновременно и примитивом, и контейнером. В нем объявлены:
q абстрактная операция примитива Работать( );
q абстрактные операции контейнера — управления примитивами-потомками Добавить(Компонент) и Удалить(Компонент), а также доступа к потомку Получить-Потомка().
Структурная составляющая паттерна Компоновщик представлена на рис. 12.51.
Рис. 12.51. Структурная составляющая паттерна Компоновщик
Из рисунка видно, что с помощью паттерна организуется рекурсивная композиция.
Класс Компонент служит простым элементом дерева, класс Компоновщик является рекурсивным элементом, а класс Лист — конечным элементом дерева. Класс Компонент служит родителем классов Лист и Компоновщик. Отметим, что класс Компоновщик является агрегатом составных частей — экземпляров класса Компонент (таким образом задается рекурсия).
Клиенты используют интерфейс класса Компонент для взаимодействия с объектами дерева. Если получателем запроса клиента является объект-лист, то он и обрабатывает запрос. Если же получателем является составной объект-компоновщик, то он перенаправляет запрос своим потомкам, возможно выполняя дополнительные действия до или после перенаправления.
Результаты. Паттерн определяет иерархии, состоящие из классов-примитивов и классов-контейнеров, облегчает добавление новых разновидностей компонентов. Он упрощает организацию клиентов (клиент не должен учитывать специфику адресуемого объекта). Недостаток применения паттерна — трудность в наложении ограничений на объекты, которые можно включать в композицию.
Обозначение паттерна Компоновщик приведено на рис. 12.52, где показано, что у него три параметра настройки — компонент, компоновщик и лист.
Настройку паттерна на графическое приложение иллюстрирует рис. 12.53.
Рис. 12.52. Обозначение паттерна Компоновщик
Рис. 12.53. Настройка паттерна Компоновщик
В этом случае основной операцией приложения становится операция Рисовать(). Подразумевается, что такая операция входит в состав каждого из подключаемых классов, то есть классов Рисунок, Прямоугольник и Графический элемент. Операции Рисовать() должны заместить операции Работать() в классах паттерна.