|
Кристаллы Если у вас имеется одна грань и вы хотите получить другую грань того же объекта, наиболее прямолинейный подход также заключается во включении операторов преобразования в грань. Упрощенный подход выглядит так: class ViewEvents { private: View* view; public: operator ViewDrawing() { return ViewDrawing(*view); } // И т.д. для других граней }; В этом маленьком С++-изме работа поручается операторной функции operator ViewDrawing() целевого вида. При малом количестве граней такое решение вполне приемлемо. С ростом количества граней число операторов преобразования возрастает в квадратичной зависимости, поскольку каждая грань должна преобразовывать ко всем остальным. Следующая модификация возвращает задачу к порядку n, где n - количество граней. Продолжая свою откровенно слабую метафору, я называю объект, который собирает и выдает грани, кристаллом (gemstone). class View; class ViewEvents; class ViewDrawing; class ViewGemstone { private: View* view; public: ViewGemstone(View* v) : view(v) {} bool operator!() { return view == NULL; } operator ViewEvents(); operator ViewDrawing(); // И т.д. }; class ViewEvents { friend class ViewGemstone; private: View* view; ViewEvents(View* v) : view(v) {} public: bool operator!() { return view == NULL; } operator ViewGemstone(); }; class ViewDrawing { friend class ViewGemstone; private: View* view; ViewDrawing(View* v) : view(v) {} public: bool operator!() { return view == NULL; } operator ViewGemstone(); }; У нас есть один объект, кристалл, который умеет генерировать все грани; каждая грань, в свою очередь, знает, как найти кристалл. Кристалл является единственным объектом, который может создавать грани, так как последние имеют закрытые конструкторы и дружат с кристаллом. Концепция кристалла чрезвычайно гибка - он может быть самостоятельным объектом, абстрактным базовым классом объекта и даже одной из граней. С первого взгляда кажется, что такое решение создает излишние неудобства для пользователя, которому приходится выполнять два последовательных преобразования типа. Наверное, кому-нибудь захочется сделать класс ViewGemstone базовым для всех остальных. Такой вариант возможен, но тогда исчезнут некоторые важные преимущества. Приведенная выше модель является абсолютно плоской; между гранями не существует отношений наследования. Благодаря этому возникает огромная степень свободы в реализации - для поддержания этих интерфейсов можно использовать наследование, делегирование и агрегирование (внедренные переменные класса). Все это с лихвой окупает одно лишнее преобразование типа. |
Copyright 2005. Климов Александр. All Right Reserved.