Скажем , у меня есть иерархия Item
классов: Rectangle, Circle, Triangle
. Я хочу иметь возможность рисовать их, поэтому моя первая возможность - добавить виртуальный Draw()
метод к каждому:
class Item {
public:
virtual ~Item();
virtual void Draw() =0;
};
Однако я хочу разделить функциональность рисования на отдельную библиотеку Draw, в то время как библиотека Core содержит только основные представления. Есть несколько возможностей, о которых я могу подумать:
1 - A, DrawManager
который берет список Item
s и должен использовать, dynamic_cast<>
чтобы решить, что делать:
class DrawManager {
void draw(ItemList& items) {
FOREACH(Item* item, items) {
if (dynamic_cast<Rectangle*>(item)) {
drawRectangle();
} else if (dynamic_cast<Circle*>(item)) {
drawCircle();
} ....
}
}
};
Это не идеально, так как он опирается на RTTI и заставляет один класс быть в курсе всех элементов в иерархии.
2 - Другой подход - отложить привлечение ответственности к ItemDrawer
иерархии ( RectangleDrawer
и т. Д.):
class Item {
virtual Drawer* GetDrawer() =0;
}
class Rectangle : public Item {
public:
virtual Drawer* GetDrawer() {return new RectangleDrawer(this); }
}
Это позволяет разделить проблемы между базовым представлением Предметов и кодом для рисования. Проблема, однако, в том, что классы Item зависят от классов рисования.
Как я могу выделить этот код для рисования в отдельную библиотеку? Является ли решение для Предметов вернуть фабричный класс некоторого описания? Однако как это можно определить, чтобы библиотека Core не зависела от библиотеки Draw?
источник
visit()
метод.Разделение, которое вы описываете - на две параллельные иерархии - по сути является образцом моста .
Вы волнуетесь, что это делает уточненную абстракцию (Rectangle и т. Д.) Зависимой от реализации (RectangleDrawer), но я не уверен, как вы могли бы избежать этого полностью.
Вы, конечно, можете предоставить абстрактную фабрику, чтобы разорвать эту зависимость, но вам все еще нужен какой-то способ сообщить фабрике, какой подкласс создать, и этот подкласс все еще зависит от конкретной уточненной реализации. То есть, даже если Rectangle не зависит от RectangleDrawer, ему все равно нужен способ сообщить фабрике о его создании, а сам RectangleDrawer все еще зависит от Rectangle.
Также стоит спросить, является ли это полезной абстракцией. Рисование прямоугольника настолько сложно, что стоит добавить другой класс, или вы действительно пытаетесь абстрагировать графическую библиотеку?
источник
RectangleDrawer
иCircleDrawer
т. Д., Возможно, не самый полезный способ абстрагирования графической библиотеки. Если вместо этого вы предоставляете набор примитивов через обычный абстрактный интерфейс, вы все равно нарушаете зависимость.Взгляните на семантику значений и основанный на понятиях полиморфизм .
Эта работа изменила мой взгляд на полиморфизм. Мы использовали эту систему, чтобы сильно повлиять на повторяющиеся ситуации.
источник
Причина возникновения проблемы в том, что объекты не должны рисовать сами. Правильное рисование чего-либо зависит от миллиарда кусочков состояния, и прямоугольник не будет иметь ни одного из этих кусочков. У вас должен быть центральный графический объект, который представляет состояние ядра, такой как буфер буферов / буфер глубины / шейдеры / и т. Д., А затем назначьте ему метод Draw ().
источник
Dog
илиCat
- объект, ответственный за их рисование, намного сложнее, чем за рисование прямоугольника.