Читайте также:
|
|
Однако иногда эта цель вступает в конфликт с принципом проектирования иерархии классов, согласно которому класс должен определять только логичные для всех его подклассах операции. Класс Component поддерживает много операций, не имеющих смысла для класса Leaf. Как же тогда предоставить для них реализацию по умолчанию?
Иногда, проявив изобретательность, удается перенести в класс Component операцию, которая, на первый взгляд, имеет смысл только для составных объектов. Например, интерфейс для доступа к потомкам является фундаментальной частью класса Composite, но вовсе не обязательно класса Leaf. Однако если рассматривать Leaf как Component, у которого никогда не бывает потомков, то мы можем определить в классе Component операцию доступа к потомкам как никогда не возвращающую потомков. Тогда подклассы Leaf могут использовать эту реализацию по умолчанию, а в подклассах Composite она будет переопределена, чтобы возвращать потомков. Операции для управления потомками довольно хлопотны, мы обсудим их в следующем разделе;
а объявление операций для управления потомками. Хотя в классе Composite реализованы операции Add и Remove для добавления и удаления потомков, но для паттерна компоновщик важно, в каких классах эти операции объявлены. Надо ли объявлять их в классе Component и тем самым делать доступными в Leaf, или их следует объявить и определить только в классе Composite и его подклассах?
Решая этот вопрос, мы должны выбирать между безопасностью и прозрачностью:
- если определить интерфейс для управления потомками в корне иерархии
классов, то мы добиваемся прозрачности, так как все компоненты удает
ся трактовать единообразно. Однако расплачиваться приходится безопас
ностью, поскольку клиент может попытаться выполнить бессмысленное
действие, например добавить или удалить объект из листового узла;
- если управление потомками сделать частью класса Composite, то безо
пасность удастся обеспечить, ведь любая попытка добавить или удалить
объекты из листьев в статически типизированном языке вроде C++ бу
дет перехвачена на этапе компиляции. Но прозрачность мы утрачиваем,
ибо у листовых и составных объектов оказываются разные интерфейсы.
В паттерне компоновщик мы придаем особое значение прозрачности, а не безопасности. Если для вас важнее безопасность, будьте готовы к тому, что иногда вы можете потерять информацию о типе и придется преобразовывать компонент к типу составного объекта. Как это сделать, не прибегая к небезопасным приведениям типов?
Можно, например, объявить в классе Component операцию Composite* GetComposite (). Класс Component реализует ее по умолчанию, возвращая нулевой указатель. А в классе Composite эта операция переопределена и возвращает указатель this на сам объект:
Структурные паттерны
class Composite;
class Component { public:
virtual Composite* GetComposite() { return 0; }
j
class Composite: public Component { public:
void Add(Component*);
// ■ ■ ■
virtual Composite* GetComposite(} { return this; }
class Leaf: public Component { I.
Благодаря операции Get Composite можно спросить у компонента, является ли он составным. К возвращаемому этой операцией составному объекту допустимо безопасно применять операции Add и Remove:
Composite* aComposite = new Composite; Leaf* aLeaf = new Leaf;
Component * aComponent; Composite* test;
aComponent = aComposite; if (test = aComponent->GetComposite()) { test->Add(new Leaf);
aComponent = aLeaf;
if (test = aComponent->GetComposite()) {
test->Add(new Leaf); // не добавит лист
Аналогичные проверки на принадлежность классу Composite в C++ выполняют и с помощью оператора dynamic_cast.
Разумеется, при таком подходе мы не обращаемся со всеми компонентами единообразно, что плохо. Снова приходится проверять тип, перед тем как предпринять то или иное действие.
Единственный способ обеспечить прозрачность - это включить в класс Component реализации операций Add и Remove по умолчанию. Но появится новая проблема: нельзя реализовать Component:: Add так, чтобы она никогда не приводила к ошибке. Можно, конечно, сделать данную операцию пустой, но тогда нарушается важное проектное ограничение; попытка
Дата добавления: 2015-09-11; просмотров: 78 | Поможем написать вашу работу | Нарушение авторских прав |