Здравствуйте, genre, Вы писали:
G>ну так предложите свой способ.
Можно использовать массив указателей на функции.
G>его размер зависит от очень большого количества параметров.
Это не так. Размер свитча вряд ли будет меняться, т.к. набор примитивов для представления фигур разных форм исчерпывается отрезком и кривой Безье 3-го порядка. Как исключение, можно добавить еще и дугу эллипса.
Здравствуйте, Cyberax, Вы писали:
C>Кривые поддерживают принципиально больше возможностей управления. И при C>этом принципиально медленнее рисуются.
Действительно, это так. Однако на этих особенностях имеет смысл концентрироваться, если они имеют существенное значение для задачи. С точки зрения фигуры эти различия не важны.
Кривые и отрезки прямых линий — это сегменты пути, который формирует контур фигуры. И потому мы смотрим на них одинаково.
Именно в этой нехитрой мысли и заключается абстрагирование. А, конечно же, не в том, чтобы наплодить иерархию классов, которая не нужна.
Здравствуйте, Дядюшка Че, Вы писали:
ДЧ>Хотелось бы вынести отделить отрисовку. То есть вынести детали реализации Draw наружу. Также хочется оставить за собой возможность абстрагироваться от HDC и поддерживать отрисовку не только на нем, но и, скажем, на поверхностях DirectX и в GDI+.
ДЧ>Думается, что реализации Draw должны располагаться в отдельных иерархиях, но быть в то же время привязанными к основной иерархии.
Т.о. есть два базавых класса: Shape и DrawContext. И для каждой пары наследников этих классов теперь нужно определить операцию(операции) взаимодействия (отрисовка, перемещение). Это есть ни что иное, как известная задача о двойной диспетчеризации. Паттерн, подходящий, для решения этой задачи — это Visitor (Посетитель). Недостаток этого подхода — необходимость модифицировать базовый класс при добавлении новых классов. Другой способ — держать таблицу обработчиков парных взаимодействий, у этого подхода тоже есть свои оригинальные решения, преимущества и недостатки.
--
Справедливость выше закона. А человечность выше справедливости.
Аноним wrote: > C>Кривые поддерживают принципиально больше возможностей управления. И при > C>этом принципиально медленнее рисуются. > Действительно, это так. Однако на этих особенностях имеет смысл > концентрироваться, если они имеют существенное значение для задачи. С > точки зрения фигуры эти различия не важны.
Ну да, истребитель Миг-29 или бумажный самолетик. Различия не важны —
ведь оба описываются конструкторскими чертежами и оба являются самолетами.
У окружности в графическом редакторе есть осмысленый параметр — радиус.
Во всех нормальных редакторах я могу напрямую менять его. А теперь
расскажите мне как это нормально сделать без аналога полиморфизма.
> *Кривые и отрезки прямых линий — это сегменты пути, который формирует > контур фигуры.* И потому мы смотрим на них *одинаково*.
Ну да. А пиксели формируют изображение на мониторе, поэтому DOS и Linux
— одно и то же.
> Именно в этой нехитрой мысли и заключается *абстрагирование*. А, конечно > же, не в том, чтобы наплодить иерархию классов, которая не нужна.
Ну так покажите простой пример: псевдокод редактора, который позволит
задавать окружности и прямоугольники. Для окружности должна быть
возможность изменения радиуса, а для квадрата — изменения длин сторон.
C>Всего 115 классов? Какая мелочь, у меня их за тысячу перевалило.
Надеюсь, Вы хорошо знаете, что делаете. И о том, как все это количество классов сопровождать, Вы тоже подумали.
C>Вы лучше подумайте как вы метод 'Draw' со switch'ем в 115 case'ов C>сопровождать будете.
Вы невнимательно читаете. Откуда Вы взяли 115 case'ов?
C>GDI, кстати, это прямо поддерживает — смотрите метод SetWorldTransform.
Такая возможность появилась в GDI недавно.
C>Расскажите как это сделать правильно и проще. Я не вижу более простого C>способа.
Чуть позже расскажу.
Кирилл Лебедев wrote: > G>ну так предложите свой способ. > Можно использовать массив указателей на функции.
Ага, это называется "восход солнца вручную". Вы просто повторите
механизм полиморфизма, но уже руками без поддержки компилятора.
Такой трюк в мире С называется vtable (virtual table ) и используется
для реализации COM-объектов (интерфейсы которых являются просто набором
указателей на функции).
> G>его размер зависит от очень большого количества параметров. > Это не так. Размер свитча вряд ли будет меняться, т.к. набор примитивов > для представления фигур разных форм исчерпывается отрезком и кривой > Безье 3-го порядка. Как исключение, можно добавить еще и дугу эллипса.
Ага, а еще нужны битмапы (для рисунков) и шрифты (для текста). Причем
шрифты бывают двух видов: растровые и векторные с соответствующими
различиями в поведении. А еще могут понадобиться примитивы заливки или
CSG-операций. А потом захочется OLE-объекты вставлять (диаграммы из MS
Graph, например). А потом...
Здравствуйте, Cyberax, Вы писали:
C>Ну да, истребитель Миг-29 или бумажный самолетик. Различия не важны — C>ведь оба описываются конструкторскими чертежами и оба являются самолетами.
С точки зрения "устройства, напичканные разными приборами" это, действительно, разные вещи. С точки зрения "устройства, которые летают" они одинаковы.
C>У окружности в графическом редакторе есть осмысленый параметр — радиус. C>Во всех нормальных редакторах я могу напрямую менять его. А теперь C>расскажите мне как это нормально сделать без аналога полиморфизма.
В Microsof Word'е, например, не можете.
Кирилл Лебедев wrote: > C>Вы прямо цитируете пример из учебника как НЕ надо писать код и для чего > C>был придуман полиморфизм. Вы это специально? > Полиморфизм — лишь один из инструментов организации кода, который в ряде > случаев не только не полезен, но и *вреден*.
Когда? Вы не привели ни одного классического примера вредности OOP (типа
"brittle base class problem").
> Недостатки полиморфизма *для данного конкретного случая* я уже привел. > Их привели и Вы в своем сообщении > http://www.rsdn.ru/Forum/Message.aspx?mid=1698098&only=1
0. Не описывает всех возможных случаев.
1. Достаточно 0).
> Сожаления о том, что там не используется полиморфизм, эмоционально > понятны, но *профессионально* "не прокатывают".
Я говорю совершенно профессионально — ваш код взят из примеров "как не
надо писать" из учебников по ООП. Привести конкретные ссылки?
> C>Простите, я знаю что говорю. Тот же GDI рисует окружности _намного_ > C>быстрее кривых Безье. Хотя бы из-за того, что не всегда нормально > C>работает аппаратное ускорение для кривых Безье. > И что с того? К чему нам такое ускорение, если оно нам *не нужно*? > Повторюсь, глифы *TrueType* и *Type1* шрифтов используют кривые Безье.
А монитор использует пиксели. И что с того?
_КАК_ вы планируете делать осмысленые манипуляции с текстом (изменение
букв, например), представленном в виде набора кривых?
Вот у вас массив действий (вправо-вверх-на-10 вправо-вниз-на-4
влево-на-4 вправо-на-4 вправо-вниз-на-4). Что это такое?
> C>Вы знаете внутренности CD? > Мне приходилось проводить research на тему того, как лучше организовать > классы фигур для редактора векторной графики.
И где этот редактор?
> C>Еще раз. PS, PDF, bitmap — это конечные _представления_. Они не > C>предоставляют нормальной поддержки для манипуляции объектами. > 1) Bitmap упомянули Вы, а не я. > 2) Что касается PS и PDF, то тут Вы ошибаетесь.
Где? Как мне в PS прочитать и изменить текст, представленый в виде
набора кривых?
> C>_КАК_ вы определите какие точки принадлежат окружности? > Преобразую в полигон и проверю.
То есть у вас в редакторе будет еще и интеллектуальный распознаватель
фигур (вместе с OCR'ом и разумным демоном, вестимо).
Здравствуйте, Cyberax, Вы писали:
C>Когда? Вы не привели ни одного классического примера вредности OOP (типа C> "brittle base class problem").
1) Я не говорил о вредности ООП вообще. Эту мысль приписали мне Вы.
2) Я говорил о вредности применения иерархии классов в данном конкретном случае. Вот ссылки на мои посты: http://www.rsdn.ru/Forum/Message.aspx?mid=1692826&only=1
C>Если вы не поняли, то в этом сообщении я привел недостатки _ОТСУТСТВИЯ_ C>полиморфизма.
Поправлюсь. В Вашем примере явственно видны недостатки подхода "по классу на каждую фигуру". Если будет полиморфизм, то недостатков станет меньше. Тем не менее, они будут.
Например: из-за того, что сегменты пути (отрезки прямых линий и кривые Безье) реализованы в виде классов, описание пути нужно хранить в виде набора указателей:
// Базовый класс для сегмента пути.class TPathSegment {...};
// Класс для отрезка.class TLineSegment : public TPathSegment {...};
// Класс для кривой Безье.class TCurveSegment : public TPathSegment {...};
// Путь - контур фигуры.class TPath
{
public:
// ...private:
std::vector<TPathSegment *> m_Segments;
};
// Фигура.class TFigure
{
public:
// ...private:
TPath m_Path;
};
Это решение обладает такими недостатками:
1) Не позволяет интерпретировать путь, как набор точек (пусть и со своими флагами). Т.е. вводит дополнительный уровень абстракции, который в данном случае не нужен. (Устранение свитча из 3 (трех!) case'ов не назовешь особой нуждой.)
2) При добавлении нового сегмента пути необходимо выделять память под сегмент, т.е. использовать оператор new. В то время, как в путь, состоящий из точек, мне достаточно "запихнуть" необходимое количество точек (1 или 3).
C>0. Не описывает всех возможных случаев. C>1. Достаточно 0).
Бездоказательно. Какие конкретно случаи оно не описывает?
Язык PostScript существует и развивается уже довольно значительное время. За все это время к пути не понадобилось добавить никакого другого примитива. Пока что обходятся отрезками и кривыми Безье.
C>Я говорю совершенно профессионально — ваш код взят из примеров "как не C>надо писать" из учебников по ООП. Привести конкретные ссылки?
Мне не нужны никакие ссылки на чей-то другой код. Поверьте, я могу и сам привести немало подобных ссылок. Если Вы не видите разницу между архитектурным решением в чужом коде и архитектурным решением, предложенным мной, и свои выводы делаете только на внешней похожести, то это просто говорит о недопонимании Вами некоторых глубинных вещей, относящихся к построению абстрактных моделей.
Чтобы выявить это недопонимание или, наоброт, выяснить, что все в порядке, и чего-то не допонимаю я, а не Вы, необходимо, чтобы Вы перечислили конкретные недостатки именно моего кода и моего решения. Так что жду от Вас конкретных аргументов.
>> И что с того? К чему нам такое ускорение, если оно нам *не нужно*? >> Повторюсь, глифы *TrueType* и *Type1* шрифтов используют кривые Безье. C>А монитор использует пиксели. И что с того?
Приведенный мною пример говорит о том, что сложные контуры (а контуры символов в шрифтах) можно описать комбинацией отрезков и кривых Безье.
Кроме того, Ваш аргумент о скорости не "прокатывает", т.к. раз она достаточна для того, чтобы рисовать шрифты, ее хватит и для того, чтобы рисовать фигуры, в которых гораздо меньше кривых, чем в шрифтах.
C>_КАК_ вы планируете делать осмысленые манипуляции с текстом (изменение C>букв, например), представленном в виде набора кривых?
Растеризация символов шрифта в последовательность контуров происходит на другом уровне абстракции. Т.е. она относится к обязанностям устройства, а не фигуры.
Но и этот пример можно использовать для подтверждения правильности моего подхода. Если следовать Вашей логике, то разработчики шрифтов (или растеризаторов для шрифтов) должны писать по классу на каждый глиф. Ведь глиф — это отдельная фигура. Тогда должно было получиться что-то вроде:
// Глиф, базовый класс.class TGlyph {...};
// Буква A.class TGlyphA : public TGlyph {...};
// Буква B.class TGlyphB : public TGlyph {...};
// Буква C.class TGlyphC : public TGlyph {...};
// И т.д.
C>Вот у вас массив действий (вправо-вверх-на-10 вправо-вниз-на-4 C>влево-на-4 вправо-на-4 вправо-вниз-на-4). Что это такое?
См. пример со шрифтами. Хотя пока что непонятно, зачем Вам в коде нужно знать, работаете ли Вы с прямоугольником или с эллипсом.
C>Где? Как мне в PS прочитать и изменить текст, представленый в виде C>набора кривых?
Текст преобразуется в контуры на уровне устройства. До этого момента с текстом работают, как с набором символов. Если же Вам (не пользователю, а Вам) необходимо различать, с какой фигурой Вы работаете, то можно использовать подход, с успехом применяющийся для текста: дать каждой фигуре определенный идентификатор (целочисленный или строковый).
Более детально о языке PostScript написано вот здесь: http://partners.adobe.com/public/developer/en/ps/psrefman.pdf
C>То есть у вас в редакторе будет еще и интеллектуальный распознаватель C>фигур (вместе с OCR'ом и разумным демоном, вестимо).
Нет. Зачем? У меня есть устройство, которое с заданной точностью преобразует мне путь в полигон.
C>Напоминаю, у нас есть _только_ набор линий.
Набор линий существует внутри фигуры. Никто не возражает, если фигура (помимо линий) будет содержать еще и название.
Здравствуйте, Кирилл Лебедев, Вы писали:
C>>Ага, одним классом с баааальшим switch'ем. КЛ>Откуда Вы взяли огромный switch?
Решил тоже поучаствовать в вашем обсуждении/споре.
Подход Cyberax по поводу отдельного класса для каждой фигуры я понял. Но я пока не понял вашего подхода. Если есть 115 фигур, то в программе по любому должно быть 115 чего-то. Классов, веток в switch'е, функций, специализаций шаблона, разделов в xml-файле или ещё чего-то.
Так чего же вы предлагаете сделать 115?
Здравствуйте, remark, Вы писали:
R>Подход Cyberax по поводу отдельного класса для каждой фигуры я понял. Но я пока не понял вашего подхода. Если есть 115 фигур, то в программе по любому должно быть 115 чего-то. Классов, веток в switch'е, функций, специализаций шаблона, разделов в xml-файле или ещё чего-то. R>Так чего же вы предлагаете сделать 115?
Сама фигура (класс TFigure) хранит:
1) Описание контура (набор отрезков и кривых Безье). Такой набор обычно называют путем.
2) Толщину бордюра.
3) Цвет бордюра.
4) Цвет заливки.
При создании фигуры шаблон описания считывается из файла. При этом происходит преобразование координат из локальной (для фигуры) системы координат в систему координат документа.
Забыл добавить. Преимущество такого подхода — мы можем добавлять новые фигуры без изменения исходного кода и, следовательно, без перекомпиляции программы.
Кирилл Лебедев wrote: > Сама фигура (класс *TFigure*) хранит: > 1) Описание контура (набор отрезков и кривых Безье). Такой набор обычно > называют *путем*. > 2) Толщину бордюра. > 3) Цвет бордюра. > 4) Цвет заливки. > При создании фигуры шаблон описания считывается из файла. При этом > происходит преобразование координат из локальной (для фигуры) системы
А теперь представьте как в вашей модели изменять радиус окружности или
шрифт у надписи.
Здравствуйте, Cyberax, Вы писали:
C>А теперь представьте как в вашей модели изменять радиус окружности или C>шрифт у надписи.
А вместо радиуса поменять диаметр слабо?
Рекомендую обратить внимание на ссылки (в них есть смысл):
Кирилл Лебедев wrote: > C>А теперь представьте как в вашей модели изменять радиус окружности или > C>шрифт у надписи. > А вместо радиуса поменять диаметр *слабо*? > Рекомендую обратить внимание на ссылки (в них есть смысл)
А на простой вопрос не можете ответить? Каким образом мне осмысленно
представить _окружность_ и как сделать ее радиус доступным для
редактирования?
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, Cyberax, Вы писали:
C>>Кривые поддерживают принципиально больше возможностей управления. И при C>>этом принципиально медленнее рисуются. А>Действительно, это так. Однако на этих особенностях имеет смысл концентрироваться, если они имеют существенное значение для задачи. С точки зрения фигуры эти различия не важны.
Все дело в том что в вашем случае — если после дизайна потребуется, например, по причинам производительности, рефакторинг, то это будет очень непростой задачей.
В моем варианте (выделение в иерархию небольшого количества сущностей) рефакторинг будет вполне реален.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Забыл добавить. Преимущество такого подхода — мы можем добавлять новые фигуры без изменения исходного кода и, следовательно, без перекомпиляции программы.
Такой подход тоже хорошо. Но тут надо чётко осознавать размер проекта и требования к сопровождаемости, т.к. в Вашем подходе вводится мощный и толстый уровень абстракции. Пока Вы будете разрабатывать формат внешнего XML файла, писать загрузку, интерпретацию и т.д. Cyberax уже успеет слабать пару десятков классов
По поводу добавления новых фигур, ну тут и кодирование фигур не уступает — ничто не мешает мне подложить новую dll'ку с фигурами без перекомпиляции основного проекта.
Ещё такой принципиальный вопрос по поводу Вашего подхода: то, что Вы описали относиться только к рисованию фигур, но с фигурами обычно связано ещё много чего: название, картинка (ну хотя это тоже можно поместить во внешний файл), способ размещения юзером (например, окружность можно размещать по 2 точкам, по 3 точкам и т.д.), способ отмены рисования фигуры, список свойств (радиус для окружности, количество точек для полигона).
В общем что я хочу сказать, в большинстве приложений с каждой фигурой связано много чего, и просто редактированием внешнего файла для добавления фигуры тут не обойтись. Как Вы предлагаете связывать с фигурой всю эту информацию?
> Хотелось бы вынести отделить отрисовку. То есть вынести детали реализации Draw > наружу. Также хочется оставить за собой возможность абстрагироваться от HDC и > поддерживать отрисовку не только на нем, но и, скажем, на поверхностях DirectX и > в GDI+.
> Думается, что реализации Draw должны располагаться в отдельных иерархиях, но > быть в то же время привязанными к основной иерархии.
> Пример надуманный, но зато довольно наглядный. Чувствую, паттерны должны помочь; > но какой/какие из них?
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>С точки зрения "устройства, напичканные разными приборами" это, действительно, разные вещи. С точки зрения "устройства, которые летают" они одинаковы.
ИМХО:
"устройства, напичканные разными приборами" — классы
"устройства, которые летают" — одинаковые интерфейсы.
Опять приходим к полиморфизму
Здравствуйте, denisku, Вы писали:
D>"устройства, напичканные разными приборами" — классы D>"устройства, которые летают" — одинаковые интерфейсы. D>Опять приходим к полиморфизму
Классы и интерфейсы — это детали реализации. Если мне удобно, я могу реализовать летающее устройство в виде класса или описать для него интерфейс в виде интерфейса или абстрактного класса. В тоже время, если опять-таки это удобно, я могу реализовать летающее устройство в виде числа или функции. Например, никого не удивляет то, что в физике траектория полета ядра в безвоздушном пространстве описывается параболой. Зачем мне класс, если мне достаточно простой формулы?
Поэтому нельзя смешивать модель и реализацию. Даже у хорошей модели может быть плохая реализация. Но если модель плохая, то реализацию ничто не спасет.
Модель построения графических редакторов, предложенная во многих учебниках по ООП, и поддерживаемая Cyberax'ом, плохая. Плохая она не потому, что это мое такое частное мнение, а потому что она не сокращает количество сущностей. Все разнообразие форм предложено инкапсулировать в иерархии. От того и плодятся класс за классом. Если бы разработчики шрифтов или цветовых моделей следовали бы этой модели, то на каждый символ и на каждый цвет пришлось бы заводить по классу. (Подробнее о шрифтах я написал здесь http://www.rsdn.ru/Forum/Message.aspx?mid=1699457&only=1
А так все разнообразие контуров символов можно свести к комбинации отрезков и кривых, а все (или почти все) разнообразие цветов можно свести к комбинациям нескольких базовых цветов.