Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Вопрос на стыке ООП, С++ и Doc/View-архитектуры

7 views
Skip to first unread message

Damir Tenisheff

unread,
Oct 31, 2004, 3:27:54 PM10/31/04
to
Рад приветствовать тебя All!

Впрочем, как будет ясно чуть позже - Doc/View архитектура в обсуждаемом примере
лишь способ упростить изложение задачи. Сразу оговорюсь, что пример упрощён
предельно - т.е. выделена та часть, которую хочется обсудить. е надо цепляться
за конкретные детали.

Дано:
Приложение, написано в рамках Doc/View архитектуры. Класс Doc хранит контейнер
объектов. Скажем:

class Doc {
:
vector<Circle> circles;
};

Класс View отвечает за отображение и расположен, естественно, в отдельном
файле. Он имеет возможность добавлять, удалять и запрашивать окружности в
документе. То есть одним из понятий, которым обмениваются Doc и View является
окружность.

В простейшем варианте (пока забудем про фабрики) объявление класса окружности
хранится в файле figures.h, который включается как в файл doc.h, так и в файл
view.h.
Однако, объявление Circle содержит один неприятный метод, а именно
class Circle {
void Draw(CDC *pDC) const;
};
то есть метод отображения (рисования) на контекст устройства.

И теперь начинаются неприятности: файл документа на стадии компиляции зависит
от файлов, ответственных за отображение. Стоит измениться объявлению функции
Draw или класса CDC и файл документа придётся перекомпилировать. А он ничего не
должен знать о рисовании вообще!

В данном (простейшем примере) всё в рамках С++ решается с использованием
forward declaration:
class CDC;
перед объявлением класса Circle. о это не всегда возможно.

Первые мысли - использовать наследование вида
class Visible {
:
virtual void Draw(CDC*) const = 0;
};

class Circle;
class VisibleCircle : public Circle, public Visible;

не дают хорошего результата:
1. Придётся задействовать полиморфизм <на ровном месте>. Здесь все должно
решаться в статике.
2. Сложно будет сделать метод добавления окружности в Doc - возможна "срезка
типа" при создании копии, поскольку документ ничего не знает про VisibleCircle
и будет создавать Circle.
3. В контейнере circles придётся хранить указатели, что уже очень плохо.

Вопросы:
1. Как правильно создать файловую иерархию, чтобы Doc и View были достаточно
независимы?
2. Как сделать так, чтобы Doc ничего не знал про отрисовку?
3. Как сделать всё это, сведя к возможному минимуму (а желательно, вообще
исключив) все копирования объектов?

Интересует красивое и эффективное решение в рамках С++.

Пункт 3 обусловлен тем, что есть красивое архитектурное решение по разделению,
но оно требует умения преобразовывать типы между собой, что для низкочастотных
взаимодействий приемлемо. А для высокочастотных (которые меня интересуют) -
нет.

Удачи в бою!.. ;)
Damir.


Maxim Kizub

unread,
Oct 31, 2004, 8:10:37 PM10/31/04
to
Sun Oct 31 2004 22:27, Damir Tenisheff wrote to All:

DT> Впрочем, как будет ясно чуть позже - Doc/View архитектура в обсуждаемом
DT> примере лишь способ упростить изложение задачи. Сразу оговорюсь, что
DT> пример упрощён предельно - т.е. выделена та часть, которую хочется
DT> обсудить. е надо цепляться за конкретные детали.

DT> Дано:
DT> Приложение, написано в рамках Doc/View архитектуры. Класс Doc хранит
DT> контейнер объектов. Скажем:

DT> class Doc {
DT> :
DT> vector<Circle> circles;
DT> };

DT> В простейшем варианте (пока забудем про фабрики) объявление класса
DT> окружности хранится в файле figures.h, который включается как в файл
DT> doc.h, так и в файл view.h.
DT> Однако, объявление Circle содержит один неприятный метод, а именно
DT> class Circle {
DT> void Draw(CDC *pDC) const;
DT> };
DT> то есть метод отображения (рисования) на контекст устройства.

Это в корне противоречит Doc/View архитектуре.
Класс Circle не должен уметь себя рисовать. Вообще. Это не его
собачье дело, на чём и как его будут рисовать. Его дело хранить
координату центра, радиус, может цвет и т.д. А рисоваться -
нет.
Поэтому метода Draw там в принципе быть не может. Hу, можно
для удобства сделать

class Device {
virtual void Draw(Circle*) = 0;
virtual void Draw(Square*) = 0;
...
}
class Circle {
virtual void Draw(Device* d) { d.Draw(this); }
}

но не более того. И в какой-нибудь Win32Device: public Device
ты можешь пихать CDC* pCD и прочие девайс-специфические
данные.

Kiev compiler - http://www.forestro.com/kiev/

0 new messages