Здравствуйте, Sinclair, Вы писали:
S> Максимум, на который можно рассчитывать — это наследование обоих от единого базового класса/интерфейса. При этом он будет настолько убогим, что не сможет иметь практической ценности.
Ну, насчет убожества третьего базового класса- здесь, пожалуй, не соглашусь. Вот, например:
class Figure
{
public:
// вот это было в примереvirtual double GetArea() const = 0;
// а вот это вполне могло бы быть...virtual void Draw( Canvas & ) const = 0;
virtual bool HasIntersection( const Figure & ) const = 0;
vurtual bool Contains( const Point & ) const = 0;
// а вот это спорно, но вполне жизненно, и ППЛ не нарушаетprotected:
Point m_disp;
double m_magnificationRatio;
double m_angle;
void ApplyAffineTransform( const Transform & ) {...}
};
AndrewVK wrote:
> G>Пример поведения реализуемого в квадрате, и *теоритически* > нереализуемого в прямогугольнике в студию. После этого и продолжим > разговор. > Получение круга, который касается всех четырех сторон. Пойдет?
Вспомнилось: "Овал — это круг, вписанный в квадрат со сторонами 3 на 4".
А если серьезно, то при желании можно представить прямоугольник как
результат преобразования квадрата.
Gaperton wrote:
> S> Максимум, на который можно рассчитывать — это наследование обоих от > единого базового класса/интерфейса. При этом он будет настолько > убогим, что не сможет иметь практической ценности. > Ну, насчет убожества третьего базового класса- здесь, пожалуй, не > соглашусь. Вот, например: > >class Figure >{ >public: > // вот это было в примере > virtual double GetArea() const = 0; > >// а вот это вполне могло бы быть... > virtual void Draw( Canvas & ) const = 0; > virtual bool HasIntersection( const Figure & ) const = 0; > vurtual bool Contains( const Point & ) const = 0; > >// а вот это спорно, но вполне жизненно, и ППЛ не нарушает >protected: > Point m_disp; > double m_magnificationRatio; > double m_angle; > > void ApplyAffineTransform( const Transform & ) {...} >}; >
Начнем критиковать c метода Draw:
Предположим, что фигура "квадрат" будет рисоваться на канвасе с помощью
четырех линий. А если завтра мы захотим добавить другой тип канваса
(например, рендерер в PostScript), в котором есть специальный
оптимизированый метод для рисования прямоугольника? С квадратом,
конечно, разницы не будет никакой, а вот с закрашеной областью,
ограниченой кривыми Безье — разница уже вполне может быть.
Поэтому метода Draw у абстрактной фигуры быть не должно, должны быть
отдельные объекты-рендереры.
Точно такие же возражения насчет интерсекторов — они тоже должны быть
выделены в отдельную иерархию. Например, для некоторых сцен может быть
построено BSP-дерево, значительно ускоряющее нахождение пересечений.
Метод Contains можно вынести в интерсекторы, но можно и оставить как
крайний случай.
Ну и m_magnification, m_angle и m_disp лучше представить в виде
однородного преобразования.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gbear, Вы писали:
G>>Пример поведения реализуемого в квадрате, и теоритически нереализуемого в прямогугольнике в студию. После этого и продолжим разговор.
AVK>Получение круга, который касается всех четырех сторон. Пойдет?
Неа...
Вариант 1. Ввести данную оп-цию в контракт квадрата. virtual Circle S.GetCircumcircle(). И перекрыть её в прямоугольнике.
Вариант 2. Строим агрегат Circle A.GetGetCircumcircle(Square s). В этом случае перекрываем R.get_Size. Например, таким образом, чтобы R.Width = base.Size.
Здравствуйте, AndrewVK, Вы писали:
G>>Пример поведения реализуемого в квадрате, и теоритически нереализуемого в прямогугольнике в студию. После этого и продолжим разговор.
AVK>Получение круга, который касается всех четырех сторон. Пойдет?
ответ пойдет, только вопрос не в тему. ведь если квадрат наследуется от прямоугольника, он вполне логично расширяет функционал!
например TStream->TFileStream. вы ведь не настаиваете на функционале "запись в файл" у базового класса?
логичный вопрос должен был звучать в обратную сторону — "какая ф-ция прямоугольника, для класса квадрат изменяет поведение базового класса прямоугольник".
Здравствуйте, Oleg A. Bachin, Вы писали: OAB>ответ пойдет, только вопрос не в тему. ведь если квадрат наследуется от прямоугольника, он вполне логично расширяет функционал!
Нет. Квадрат, будучи отнаследованным от прямоугольника, исказит его контракт.
Например, удвоение ширины учетверит площадь вместо удвоения. OAB>например TStream->TFileStream. вы ведь не настаиваете на функционале "запись в файл" у базового класса?
Нет. TStream не дает никаких обещаний, которые нарушит TFileStream. А вот TMemoryStream ты никак от TFileStream не отнаследуешь, как, впрочем, и наоборот. OAB>логичный вопрос должен был звучать в обратную сторону — "какая ф-ция прямоугольника, для класса квадрат изменяет поведение базового класса прямоугольник".
Логичный вопрос, в общем-то, такой: "Почему новички отказываются читать литературу"? Все по поводу кругов/эллипсов и квадратов/прямоугольников было обсосано до мельчайших косточек. Ничего нового в этой области изобрести не удастся.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Oleg A. Bachin, Вы писали: OAB>>ответ пойдет, только вопрос не в тему. ведь если квадрат наследуется от прямоугольника, он вполне логично расширяет функционал! S>Нет. Квадрат, будучи отнаследованным от прямоугольника, исказит его контракт. S>Например, удвоение ширины учетверит площадь вместо удвоения. OAB>>например TStream->TFileStream. вы ведь не настаиваете на функционале "запись в файл" у базового класса? S>Нет. TStream не дает никаких обещаний, которые нарушит TFileStream. А вот TMemoryStream ты никак от TFileStream не отнаследуешь, как, впрочем, и наоборот. OAB>>логичный вопрос должен был звучать в обратную сторону — "какая ф-ция прямоугольника, для класса квадрат изменяет поведение базового класса прямоугольник". S>Логичный вопрос, в общем-то, такой: "Почему новички отказываются читать литературу"? Все по поводу кругов/эллипсов и квадратов/прямоугольников было обсосано до мельчайших косточек. Ничего нового в этой области изобрести не удастся.
э... я вообщет и не спорю — я все прекрасно понимаю... только одно не понятно.
не желающий прочитать литературу и понять базовые принципы ООП, еще может унаследовать квадрат от прямоугольника, но обратное наследование я вообще не представляю кто может сделать! я лишь на этом акцентировал внимание
Здравствуйте, Gaperton, Вы писали:
G>> Так же, будет существовать и программа (агрегат), которая будет вести себя корректно.
G>Принцип подстановки Лисков, которому должны удовлетворять подтипы, требует корректного поведения от любой программы при замене типа на подтип. Чувствуете разницу между "любой" и "будет существовать"?
Хм... я так понимаю Вы об этом:
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T — BarbaraLiskov, Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (May, 1988). .
Если да, то прежде чем... желательно бы услышать от Вас же расшифровку понятий: all programs, и behavior is unchanged И лучше бы перед тем как... почитать таки оригинал. Это поможет снять некоторые вопросы. И может быть Вам после этого станет ясно, почему, например, уже в определении ПЛВ нет этих терминов.
Далее... может быть... очень может быть, что мы изначально друг друга не понимаем... мой косяк. Попытаюсь исправиться... В рассуждениях о варианте S<-R я виду речь о т.н. behavioural subtyping... очень надеюсь на то, что Вам знаком этот термин.
G>> Ну не красиво же... право слово. G>Вы меня убиваете, право слово. Я вам тут считай даром 50 баксов предлагаю , само по себе нивираятна блаародный и красивый поступок, вот. Причем все честно — Синклер человек строгих правил и не допустит тут никакого жульничества. А вы, можно сказать, раздающему, то есть мне, прикуп в морду швыряете. За что?!
За навязчивость Яж Вам открытым текстом сказал буквально: "Знаю-знаю... давайте об этом не будем".
<Следующие два пункта пока поскипаны. Ввиду того, что мы с Вами, пока (надеюсь) разговариваем о разных вещах.>
G>Я ожидал услышать в ответ: я согласен. Вот вам моя программа. И, чуть позже: куда переводить деньги? 50 баксов, сами понимаете, не лишние.
G>>>>С чего Вы взяли что "квадрат" подмножество "прямоугольников"?!
G>Слушайте, я вам привел определение подтипа из теории типа.
Вот с этого места можно по подробней... Что за теория. Работы по... авторов. Буду весьма презнателем. На сей момент, как это не прескорбно, единственная известная мне "теория типов" не имеет никакого отношения к OOA, и уж тем более к OOD.
G>Прямоугольник, определенный так как это сделано в задаче, не может являться подтипом квадрата. Он нарушает инварианты базового класса, или принцип подстановки Лисков, что по сути одно и то же. Поэтому я не могу сказать, что прямоугольник подтип квадрата. Но вам — можно.
1. Где Вы в условиях задачи усмотрели "определение" прямоугольника?! Тем более "такое как"?
2. Ещё раз извиняюсь за то, что сразу не указал Вам на то, что все рассуждения о варианте S<-R ведутся мной в терминах BS.
G>Нет, не тяжело, я видел в жизни много плохо спроектированых систем. Мне будет тяжело такую программу поддерживать и развивать. В определенный момент (который довольно скоро наступит) ее придется просто выбросить (так будет существенно дешевле) как ярко выраженый образчик хрупкого дизайна, трещащий по швам при любой модификации.
Хм... М'сье считает "хрупкость" безусловным злом?!
Ну да вопрос-то не в этом... Хрупкий дизайн, может и не противоречить чему-либо. Главное, что бы шаг в сторону повышения хрупкости был осознаным.
Oleg A. Bachin,
AVK>> Получение круга, который касается всех четырех сторон. Пойдет?
OAB> ответ пойдет, только вопрос не в тему. ведь если квадрат наследуется OAB> от прямоугольника, он вполне логично расширяет функционал! например OAB> TStream->TFileStream. вы ведь не настаиваете на функционале "запись в OAB> файл" у базового класса?
Это ровно обратный пример. В данном случае предлагали унаследовать
прямоугольник от квадрата:
Вы разве не согласны с тем, что тип — суть поведение? Разве Вы не согласны
с тем, что "вниз по иерархии" реализуемое типами поведение должно расширяться?
Как минимум — специализироваться? Так объясните мне — почему, если все поведение
квадрата, реализуемо прямоугольником, я не могу сказать, что прямоугольник
"подтип" квадрата?
Т.е., если адаптировать твою аналогию, получится class TStream : TFileStream.
OAB> логичный вопрос должен был звучать в обратную сторону — "какая ф-ция OAB> прямоугольника, для класса квадрат изменяет поведение базового класса OAB> прямоугольник".
Для квадрата, унаследованного от прямоугольника, примеры "неправильных"
функций, ломающих программы, использующие прямоугольники, в случае их
замены на квадраты (прямое нарушение LSP), уже приводились: SetWidth,
SetHeight и т.п.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, gbear, Вы писали:
G>Хм... я так понимаю Вы об этом:
G>
G>If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T — BarbaraLiskov, Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (May, 1988). .
G>Если да, то прежде чем... желательно бы услышать от Вас же расшифровку понятий: all programs, и behavior is unchanged
Специально для вас — переведу на русский весь абзац. Кстати, когда даете ссылку, неплохо давать ссылку на источник. Это так, к слову.
Если для любого o1 <- S найдется такой o2 <- T, что для любой программы p <- P, определенной в терминах T поведение p не изменится при замене o1 на o2, то S является подтипом T.
G>И лучше бы перед тем как... почитать таки оригинал. Это поможет снять некоторые вопросы. И может быть Вам после этого станет ясно, почему, например, уже в определении ПЛВ нет этих терминов.
О, у меня появился вопрос, который надо снять. А что такое "определение ПЛВ"? Вы разъясните пожелуйста, или дайте ссылку на оригинал, а то вдруг это какой-нибудь очередной килограмм на ампер с сайта udaff.com, и мне следует обидиться?
G>Далее... может быть... очень может быть, что мы изначально друг друга не понимаем... мой косяк. Попытаюсь исправиться...
Отчего же, я понимаю вас очень хорошо.
G>В рассуждениях о варианте S<-R я виду речь о т.н. behavioural subtyping... очень надеюсь на то, что Вам знаком этот термин.
Я с самого начала давал именно поведенческое определение подтипа, очень странно, что вы этого не заметили. Ну да ладно, давайте сюда ваше определение "behavioral subtyping". И давайте посмотрим, каким образом оно сделает вашу ошибку правильным решением.
G>Ну да вопрос-то не в этом... Хрупкий дизайн, может и не противоречить чему-либо. Главное, что бы шаг в сторону повышения хрупкости был осознаным.
Мы таких на работу не берем с самого начала. Поэтому не приходится и увольнять — очень удобно.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, mibe, Вы писали:
M>>Почитайте UML Nontation Guide: M>> *есть отношение обобщение — отношение частного и общего (is a) M>> *есть наследование — передача признаков от одного к другому
S>Че-то первый раз слышу. Где конкретно читать?
Я прогнал. Вот что нашёл.
...
5.28 GENERALIZATION
5.28.1 Semantics
Generalization is the taxonomic relationship between a more general element and a more specific
element that is fully consistent with the first element and that adds additional information. It is used
for classes, packages, use cases, and other elements.
mefrill,
G>> Вопрос: Кто из них родитель, а кто предок? Почему?[/i]
m> Родитель прямоугольник. Потому, что каждый квадрат является m> прямоугольником, но не каждый прямоугольник является квадратом.
Это верно для математики, т.к. она "рассуждает" в терминах свойств, и в общем
случае неверно для программирования, т.к. оно "рассуждает" в терминах поведения.
Например, в программировании изменение свойства "ширина" данного прямоугольника --
вполне нормальная ситуация. В математике же соответствующей операцией является
создание нового прямоугольника, с новыми свойствами. Соответственно, нужно очень
осторожно переносить отношения между понятиями, принятые в математике, на
отношения между понятиями, вводимыми в программировании.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, mibe, Вы писали:
M>>А еще бывают языки типа cecil где класс можно декларативно определить (типа квадрат это прямоугольник у которого длина=ширина) и конкретный объект классифицируется динамически на основе такого определения.
V>Я бы и на обычном языке программирования не вводил бы отдельный класс для квадрата.
а я бы сделал
class Rectangle // прямоугольник
class MutableRectangle extends Rectangle // прямоугольник, который можно изменять (добавляются сеттеры)
class Square extends Rectangle // квадрат
class MutableSquare extends Square // квадрат, который можно изменять
Как я понимаю частью спецификации прямоугольника является то, что длины его сторон можно менять независимо друг от друга. Стороны квадрата, естественно нельзя менять независимо, они всегда должны быть равны.
Т.е. мы изначально имеем два противоположных условия. Ну, тогда о какой полиморфной работе с изменением длин сторон вообще может идти речь ?
Чтение длин — да, можно сделать полиморфно, при помощи общего интерфейса. Кстати, довольно-таки распротраненный случай, когда чтение состояния объектов различных типов может быть сделано через один интерфейс, а вот изменения делаются разными способами.
Единственный способ извратиться и унаследовать одно от другого, имхо, такой:
class Rectangle
{
public int Width
{
get { return _width; }
}
public int Height
{
get { return _height; }
}
public virtual void SetBounds(int width, int height)
{
_width = width;
_height = height;
}
private int _width;
private int _height;
}
class Square: Rectangle
{
public override void SetBounds(int width, int height)
{
if (width != height)
throw Exception("...");
base.SetBounds(width, height);
}
}
Но, здесь нарушается другой принцип. Так называемый Open-Closed принцип. Так как мы класс Rectangle спроектировали специально из расчета наличия класса Square.
Итак повторю, что мы изначально имеем два противоположных условия. И мне непонятно в какой-такой правильно спроектированной с точки зрения ООП программе может потребоваться полиморфное изменение длин сторон при наличии таких условий.
ПК>Это верно для математики, т.к. она "рассуждает" в терминах свойств, и в общем ПК>случае неверно для программирования, т.к. оно "рассуждает" в терминах поведения.
Не уверен. Мне кажется, что понятие объекта выведено из понятия машины Тьюринга. Алгоритм в ООП представляет собой совокупность трех фаз: инициализацию объектов, обмен сообщениями и завершение. Результат работы программы есть совокупность состояний объектов-участников. С этой точки зрения, объекты представля.т собой просто объединения состояний машины Тьюринга. Каждое состояние задается соответствующим свойством, а значение состояния — значением свойства. В противном случае, непонятно как можно доказать корректность или некорректность того или иного алгоритма или вообще, возможность алгоритмизации данной задачи в терминах ООП. С этой точки зрения, что же представляют собой объекты-совокупности состояний? В программной модели они отражают онтологию мира, для которого идет моделирование. Что же такое онтология? Это, прежде всего, иерархия отношений. Из них, отношение "быть чем-то" является основным. На теоретико-множественном языке оно формируется как "принадлежность". Принцип Лисков как раз и закрепляет эту самую иерархию как принцип моделированния. В любом случае, необходимо четко определить что такое "оно "рассуждает" в терминах поведения", иначе будет большая путаница.
ПК>Например, в программировании изменение свойства "ширина" данного прямоугольника -- ПК>вполне нормальная ситуация. В математике же соответствующей операцией является ПК>создание нового прямоугольника, с новыми свойствами. Соответственно, нужно очень ПК>осторожно переносить отношения между понятиями, принятые в математике, на ПК>отношения между понятиями, вводимыми в программировании.
Согласен, все зависит от точки зрения. Но, мое глубокое убеждение состоит в том, что ООП как раз и введено специально для того, чтобы привнести рассуждения "математического типа", т.е. типа статического, описательного, в мир динамический, детерминированный и поведенческий. Отсюда и дуализм восприятия понятия "объект". Но, первичным при моделированнии, мне кажется, все же должен быть описательный подход. Т.е. сначала вводим понятия, определяем их свойства и отношения между ними, а потом начинаем интерпретировать эти понятия поведенчески, как машины состояний. Иначе нет совершенно никакого смысла в ООП, все можно запрограммировать простой тьюринговской машиной, т.е. совокупностью процедур.
Относительно приведенного примера не совсем согласен. Под прямоугольником в математике (по крайней мере, в элементарной геометрии) понимается как конкретный прямоугольник, так и некий абстрактный прямоугольник, представляющий собой совокупность некоторого множества прямоугольников. Например, когда я пишу "возмем прямоугольник ABCD, имеющий ширину 2 см и длину 5 см", то имею ввиду кокретный прямоугольник. А когда я пишу "пусть ABCD — некоторый прямоугольник, у которого ширина в два раза больше высоты", то я имею ввиду уже совокупность некоторых прямоугольников. В программировании это четко разделено понятиями объекта и его экземпляра, а в обыденном языке, частью которого является язык математический — нет. Экземпляр объекта прямоугольника не является сам прямоугольником, он лишь указывает, т.е. выделяет некоторый прямоугольник во множестве всех прямоугольников, и изменение ширины экземпляра объекта приводит только к тому, что в результате выделяется новый прямоугольник с требуемыми свойствами. Совсем непонятно также, что такое "создание нового прямоугольника, с новыми свойствами". Элементарная геометрия не создает и не уничтожает прямоугольники и вообще, любые сущности. Иначе, пришлось бы иметь дело с понятием времени, что в корне повлияло бы на сущность логики, применяющейся в математических рассуждениях.
Здравствуйте, Cyberax, Вы писали:
C>Начнем критиковать c метода Draw:
Все бы вам критиковать.
C>Предположим, что фигура "квадрат" будет рисоваться на канвасе с помощью C>четырех линий. А если завтра мы захотим добавить другой тип канваса C>(например, рендерер в PostScript), в котором есть специальный C>оптимизированый метод для рисования прямоугольника?
Кто вам сказал, какой интерфейс предполагается в канвасе? У меня что-то на эту тему сказано? Может быть, у меня в базовом классе Canvas всегда определен метод для рисования прямоугольника через линии, а в подклассах (например, для postscript) он перекрывается с целью оптимизиции?
C> С квадратом, C>конечно, разницы не будет никакой, а вот с закрашеной областью, C>ограниченой кривыми Безье — разница уже вполне может быть.
C>Поэтому метода Draw у абстрактной фигуры быть не должно, должны быть C>отдельные объекты-рендереры.
Все сильно зависит от того, что именно мы пишем. Говорить вообще что должно или не должно быть можно только опираясь на всякие LSP, все остальное сильно зависит от задачи. Например, что если абстактная фигура состоит из нескольких элементарных фигур? Где отрисовку будем писать? В нашем случае есть где.
C>Точно такие же возражения насчет интерсекторов — они тоже должны быть C>выделены в отдельную иерархию. Например, для некоторых сцен может быть C>построено BSP-дерево, значительно ускоряющее нахождение пересечений.
Опять же, не понимаю, какие у вас могут быть возражения к дизайну в условиях отсутствия задачи. И еще мне не вполне понятно, как именно мне помешает умение фигур находить свои пересечения использовать в оптимизированном случае BSP деревья.
C>Метод Contains можно вынести в интерсекторы, но можно и оставить как C>крайний случай.
C>Ну и m_magnification, m_angle и m_disp лучше представить в виде C>однородного преобразования.
А вот это безусловно так.
Здравствуйте, stalcer, Вы писали:
S>Единственный способ извратиться и унаследовать одно от другого, имхо, такой:
[skipped]
S>Но, здесь нарушается другой принцип. Так называемый Open-Closed принцип. Так как мы класс Rectangle спроектировали специально из расчета наличия класса Square.
Во-первых, здесь нарушается все тот же LSP, так как подпихивание квадрата в программу, корректно работающую с прямоугольниками, может легко ее сломать. Так что так делать не стоит. Во-вторых, единственный корректный способ извратиться существует — это сделать метод SetBounds константным, возвращающим новый экземпляр с измененными границами. Надщо немного изменить ваш пример — вот так (C++):
virtual Rectangle SetBounds(int width, int height) const;
Такое решение ничего не нарушает, кроме того, что лишает ваш объект состояния. Что тоже вполне может аукнуться в будущем, как только вам понядобятся фигуры, имеющие друг на друга циклические ссылки. Но в данном примере все будет хорошо.
Павел Кузнецов wrote:
> Например, в программировании изменение свойства "ширина" данного > прямоугольника -- > вполне нормальная ситуация. В математике же соответствующей операцией > является > создание нового прямоугольника, с новыми свойствами. Соответственно, > нужно очень > осторожно переносить отношения между понятиями, принятые в математике, на > отношения между понятиями, вводимыми в программировании.
Либо использовать языки, где изменение свойства достигается созданием
нового объекта, например: Clean, OCaml, Haskell. Часто математические
понятия очень хорошо ложатся на функциональные языки.