четверг, 8 мая 2008 г.

Нетрадиционный полиморфизм

Обычно, для реализации полиморфного поведения используется наследование. Есть базовый(абстрактный) класс, который определяет общий интерфейс. Есть потомки, конкретизирующие его поведение. Это удобно тогда, когда в иерархии находятся однородные объекты, например, встречающийся в каждом учебнике по программированию пример с граф. редактором и геометричесскими фигурами. Базовый класс Shape, и его потомки - Line, Rectangle, Circle etc. При таком подходе, иногда возникает необходимость того, что-бы в иерархии присутствовали не совсем однородные объекты, например нам понадобилось в граф. редакторе создавать комментарии и ссылки на другие документы (Shape, Line, Rectangle, Circle, TextNote???, Link???). В этом случае возникают проблемы с общим интерфейсом (у классов TextNote и Link есть аттрибуты, которых нет у остальных классов, в то-же время некоторые аттрибуты Shape не имеют для них смысла, но несмотря на это, концептуально они часть рисунка - потомки класса Shape), которые обычно решаются приведением типа вниз по иерархии (от предка к потомку) или паттерном Visitor (ну, или антипаттерном Blob :-D). Но есть еще один подход к этой проблемме, если нам и так нужно использовать информацию о типах (при upcast-е она в любом случае используется, так-же как и при посещении экземпляра объекта в рамках шаблона Visitor), то почему-бы не построить на этом архитектуру приложения? Я серьезно...:) Например можно поместить объект в контейнер boost::variant. Для доступа к нему использовать boost::static_visitor. Особого оверхэда здесь быть недолжно, так как никаких виртуальных функций нам теперь не нужно(как и наследования). Инкапсуляция немного хуже чем при работе через указатель на базовый класс, но я ведь говорю об особом случае, когда уже только через базовый класс работать нельзя. Зато нет сквозных зависимостей по всей иерархии классов, так как наследования теперь тупо нет :-D. Но, есть одно существенное ограничение, это все будет похо работать для иерархий с большим колличеством разных типов объектов, так как все они должны быть известны заранее (для variant-a, нужно задать список типов, которые он может хранить). Однако, для небольших, но "трудных" для декомпозиции иерархий объектов, это похоже на решение.

1 комментарий:

Анонимный комментирует...
Этот комментарий был удален администратором блога.