Gaperton wrote:
> C>Предположим, что фигура "квадрат" будет рисоваться на канвасе с помощью > C>четырех линий. А если завтра мы захотим добавить другой тип канваса > C>(например, рендерер в PostScript), в котором есть специальный > C>оптимизированый метод для рисования прямоугольника? > Кто вам сказал, какой интерфейс предполагается в канвасе? У меня > что-то на эту тему сказано? Может быть, у меня в базовом классе Canvas > *всегда *определен метод для рисования прямоугольника через линии, а в > подклассах (например, для postscript) он перекрывается с целью > оптимизиции?
Тогда зачем нужен метод Draw у квадрата, если он просто будет
делегировать вызов канвасу? Зачем увеличивать связность объектной модели?
> C>С квадратом, конечно, разницы не будет никакой, а вот с закрашеной > областью, > C>ограниченой кривыми Безье — разница уже вполне может быть. > C>Поэтому метода Draw у абстрактной фигуры быть не должно, должны быть > C>отдельные объекты-рендереры. > Все сильно зависит от того, что именно мы пишем. Говорить вообще что > должно или не должно быть можно только опираясь на всякие LSP, все > остальное сильно зависит от задачи. Например, что если абстактная > фигура состоит из нескольких элементарных фигур? Где отрисовку будем > писать? В нашем случае есть где.
Точно так же — в рендерерах. Делаем CompositeRenderer и вперед с песней.
Говорю из своего опыта автора графического редактора, кстати.
> C>Точно такие же возражения насчет интерсекторов — они тоже должны быть > C>выделены в отдельную иерархию. Например, для некоторых сцен может быть > C>построено BSP-дерево, значительно ускоряющее нахождение пересечений. > Опять же, не понимаю, какие у вас могут быть возражения к дизайну в > условиях отсутствия задачи. И еще мне не вполне понятно, как именно > мне помешает умение фигур находить свои пересечения использовать в > оптимизированном случае BSP деревья.
Не помешает, просто ненужно усложнит реализацию фигур.
cranky wrote:
> C>А если серьезно, то при желании можно представить прямоугольник как > C>результат преобразования квадрата. > ... как и произвольный параллелепипед. Всего лишь надо матрицу 2х2 > хранить в объекте. Интересно, это применимо где-нибудь на практике?
Такое вполне применимо, по-моему в Inkscape так и сделано.
Кстати, для двумерного случая хранят обычно не матрицу 2х2, а 3х3 — так
называемую матрицу однородного преобразования. В отличие от матрицы 2х2,
она позволяет задавать еще и параллельный перенос.
Здравствуйте, Cyberax, Вы писали:
C>Gaperton wrote:
>> C>Предположим, что фигура "квадрат" будет рисоваться на канвасе с помощью >> C>четырех линий. А если завтра мы захотим добавить другой тип канваса >> C>(например, рендерер в PostScript), в котором есть специальный >> C>оптимизированый метод для рисования прямоугольника? >> Кто вам сказал, какой интерфейс предполагается в канвасе? У меня >> что-то на эту тему сказано? Может быть, у меня в базовом классе Canvas >> *всегда *определен метод для рисования прямоугольника через линии, а в >> подклассах (например, для postscript) он перекрывается с целью >> оптимизиции?
C>Тогда зачем нужен метод Draw у квадрата, если он просто будет C>делегировать вызов канвасу? Зачем увеличивать связность объектной модели?
Во-первых — затем, что рисовать надо именно фигуры, а не что-нибудь еще. Во-вторых, фигура совсем не обязана быть настолько простой, чтобы маппится 1-в-1 в вызов метода канваса. В-третьих, фигура знает, где она расположена, и вообще, много чего знает. В-четвертых, ваше предложение не по делу сложнее, чем этот вариант, при сомнительных плюсах.
>> C>С квадратом, конечно, разницы не будет никакой, а вот с закрашеной >> областью, >> C>ограниченой кривыми Безье — разница уже вполне может быть. >> C>Поэтому метода Draw у абстрактной фигуры быть не должно, должны быть >> C>отдельные объекты-рендереры. >> Все сильно зависит от того, что именно мы пишем. Говорить вообще что >> должно или не должно быть можно только опираясь на всякие LSP, все >> остальное сильно зависит от задачи. Например, что если абстактная >> фигура состоит из нескольких элементарных фигур? Где отрисовку будем >> писать? В нашем случае есть где.
C>Точно так же — в рендерерах. Делаем CompositeRenderer и вперед с песней. C>Говорю из своего опыта автора графического редактора, кстати.
По вашим словам заметно, что вы так делали — вы говорите как надо, а не почему. Тем не менее, я не понимаю, как вы собираетесь переключать "рендереры" для фигур разных типов (виртуальный метод Draw для этого не подойдет? А что подойдет?). И совершенно не понимаю, зачем выделать рендерер в отдельный класс. Может, конечно, понадобиться. Но при каких условиях — не понятно.
>> C>Точно такие же возражения насчет интерсекторов — они тоже должны быть >> C>выделены в отдельную иерархию. Например, для некоторых сцен может быть >> C>построено BSP-дерево, значительно ускоряющее нахождение пересечений. >> Опять же, не понимаю, какие у вас могут быть возражения к дизайну в >> условиях отсутствия задачи. И еще мне не вполне понятно, как именно >> мне помешает умение фигур находить свои пересечения использовать в >> оптимизированном случае BSP деревья.
C>Не помешает, просто ненужно усложнит реализацию фигур.
С чего бы это?
mefrill,
> ПК> Это верно для математики, т.к. она "рассуждает" в терминах свойств, и в общем случае неверно для программирования, т.к. оно "рассуждает" в терминах поведения.
> Не уверен. Мне кажется, что понятие объекта выведено из понятия машины Тьюринга. <...>
Я не говорил, что программирование не оперирует состоянием объектов. Моделирование в терминах поведения не отрицает наличия состояний. Совершенно согласен с твоей трактовкой поведения как сменяющих друг друга состояний. Просто, в программировании, по сравнению с математикой (той ее "статичной" частью, откуда пришло понятие квадрата как разновидности прямоугольника), акцент смещается с состояний как таковых на "законы" переходов между ними. Т.е. более важными становятся "инкременты" между состояниями, т.е. события, действия, — одним словом, поведение, т.к. именно оно приводит к "наблюдаемым явлениям": печать данных в консольном приложении, запись в файл, отрисовка картинки, запись в порт, управляющий манипулятором и т.п.
> Алгоритм в ООП представляет собой совокупность трех фаз: инициализацию объектов, обмен сообщениями и завершение. Результат работы программы есть совокупность состояний объектов-участников.
Ну, это если свести программирование к вычислительным задачам, главным результатом которых является набор некоторых конечных данных. И то, и то с точки зрения использования программы более существенным является ее воздействие на состояние окружения, а не ее внутреннее состояние (см. выше). Если же посмотреть на области, связанные с разнообразными процессами, то конечное состояние программы (равно как и внешнего окружения) как совокупности состояний объектов уступит приоритет последовательности состояний программы, и появятся понятия времени, допустимых переходов из данного состояния и т.п. Примеры: работа GUI, воспроизведение видео, системы управления и т.п.
> отношение "быть чем-то" является основным. На теоретико-множественном языке оно формируется как "принадлежность". Принцип Лисков как раз и закрепляет эту самую иерархию как принцип моделированния. В любом случае, необходимо четко определить что такое "оно "рассуждает" в терминах поведения", иначе будет большая путаница.
Говоря о «"рассуждении" в терминах поведения» я имел в виду, что в программировании, при проектировании классов, более существенными характеристиками объектов являются не его текущее состояние (object state), а набор допустимых состояний объекта (class invariants) и набор допустимых переходов из данного состояния в другие состояния (behavior), равно как принципиальной является и последовательность переходов между состояниями объекта. Противопоставляя это математике я имел в виду, что математика (по крайней мере, в части определения прямоугольников/квадратов) сосредотачивается на статических отношениях, как бы акцентируясь на состояниях-"снимках" объекта, если смотреть с точки зрения программирования.
Описание допустимых трансформаций всех возможных состояний объекта и есть его "поведение", задаваемое классом/типом данного объекта. Немаловажным с точки зрения упомянутого отличия от математики является и понятие identity объекта: для программирования, помимо прочего, существенным является то, какой именно объект перешел в какое именно состояние, а не просто рассмотрение отношений между состояниями, выражаемыми свойствами.
Барбара Лисков в соответствующей статье как раз и говорит о поведении, выделяя его как необходимое условие для существования отношения "is a subtype" между типами:
A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property [6]: 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.
> мое глубокое убеждение состоит в том, что ООП как раз и введено специально для того, чтобы привнести рассуждения "математического типа", т.е. типа статического, описательного, в мир динамический, детерминированный и поведенческий. Отсюда и дуализм восприятия понятия "объект".
Объектно-ориентированное программирование родилось в результате попыток уменьшения сложности (information hiding), локализации изменений состояния (encapsulation). Как говорят, произошло это в процессе программирования систем симуляции, и именно в результате попыток выделить характерные черты в поведении сходных объектов. Т.е. ООП берет корни скорее в инженерных истоках программирования, чем в математических. К математике ближе "чистое" функциональное программирование, без мутирующих операций и т.п.
> первичным при моделированнии, мне кажется, все же должен быть описательный подход. Т.е. сначала вводим понятия, определяем их свойства и отношения между ними, а потом начинаем интерпретировать эти понятия поведенчески, как машины состояний.
На практике получается не вполне так. Обычно, для того, чтобы выделить свойства объетов, нужно, все-таки, сначала проанализировать их поведение (как говорят, собрать use cases).
> Иначе нет совершенно никакого смысла в ООП, все можно запрограммировать простой тьюринговской машиной, т.е. совокупностью процедур.
"В теории, нет разницы между теорией и практикой. На практике она есть."
Запрограммировать "простой тьюринговской машиной" можно все, только если мы об этом "всем" все знаем, и если это "все" в процессе, пока мы пытаемся его замоделировать, не изменяется. На практике мы узнаем об этом "всем" по мере, пока мы его моделируем, много нового, и в этом случае локализация поведения отдельных фрагментов состояния оказывается очень практичной.
> Относительно приведенного примера не совсем согласен. Под прямоугольником в математике (по крайней мере, в элементарной геометрии) понимается как конкретный прямоугольник, так и некий абстрактный прямоугольник, представляющий собой совокупность некоторого множества прямоугольников. Например, когда я пишу "возмем прямоугольник ABCD, имеющий ширину 2 см и длину 5 см", то имею ввиду кокретный прямоугольник. А когда я пишу "пусть ABCD — некоторый прямоугольник, у которого ширина в два раза больше высоты", то я имею ввиду уже совокупность некоторых прямоугольников. В программировании это четко разделено понятиями объекта и его экземпляра
Скорее, понятиями классов/типов и их экземпляров (объектов).
> Экземпляр объекта прямоугольника не является сам прямоугольником, он лишь указывает, т.е. выделяет некоторый прямоугольник во множестве всех прямоугольников, и изменение ширины экземпляра объекта приводит только к тому, что в результате выделяется новый прямоугольник с требуемыми свойствами.
Вот это и есть то, что я назвал "создание нового прямоугольника, с новыми свойствами". Для математики объектом изучения (по крайней мере, в случае прямоугольников/квадратов) является некоторый "снимок" жизни объекта программистского, его конкретное состояние. Т.е., конечно, математика включает изучение переходов между состояниями (трансформации и т.п.), но, как ты верно указал выше, когда дело доходит до конкретных прямоугольников и квадратов, с точки зрения математики проще называть прямоугольником/квадратом некоторое конкретное состояние, и считать, что трансформация приводит к созданию (если угодно, выделению) нового прямоугольника/квадрата, или, на другом языке, описывает соответствие между заданными прямоугольниками/квадратами.
С точки же зрения программирования часто удобнее называть прямоугольником/квадратом не отдельное состояние, а всю совокупность его состояний (object lifetime), сменяющихся в некоторой заданной области его хранения (region of storage), идентифицируемой данным объектом (object identity).
> Совсем непонятно также, что такое "создание нового прямоугольника, с новыми свойствами". Элементарная геометрия не создает и не уничтожает прямоугольники и вообще, любые сущности. Иначе, пришлось бы иметь дело с понятием времени, что в корне повлияло бы на сущность логики, применяющейся в математических рассуждениях.
Именно об этом я и говорил, упоминая необходимость критического использования выводов математики (в частности, и элементарной геометрии) в программировании.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Gaperton, Вы писали:
G>Здравствуйте, gbear, Вы писали:
G>Специально для вас — переведу на русский весь абзац. Кстати, когда даете ссылку, неплохо давать ссылку на источник. Это так, к слову.
G>
G>Если для любого o1 <- S найдется такой o2 <- T, что для любой программы p <- P, определенной в терминах T поведение p не изменится при замене o1 на o2, то S является подтипом T.
Ой спасибо А я-то, тупил...
Тем не менее, таки, попрошу дать определение хотя бы "поведение p не изменится". Если у нас программа рисовала квадраты... и, вдруг, начала рисовать прямоугольники — изминилось или нет её поведение? Почему?
Насчет ссылок — а чем Вас библиографическая не устраивает?! Тем более, я сильно сомневаюсь, что это где-то ещё можно найти в открытом доступе.
G>>И лучше бы перед тем как... почитать таки оригинал. Это поможет снять некоторые вопросы. И может быть Вам после этого станет ясно, почему, например, уже в определении ПЛВ нет этих терминов. G>О, у меня появился вопрос, который надо снять. А что такое "определение ПЛВ"? Вы разъясните пожелуйста, или дайте ссылку на оригинал, а то вдруг это какой-нибудь очередной килограмм на ампер с сайта udaff.com, и мне следует обидиться?
Вы меня удивляете ей богу... "Принцип Подстановки Лисков" Вам значит известен, а вот "Подтипы Лисков-Винг" (Liskov Wing Subtyping) значит нет?! Весело блин...
Subtype Requirement: Let phi(x) be a property provable about objects x of type T. Then phi(y) should be true for objects y of type S where S is a subtype of T.
Например, в "A Behavioral Notion of Subtyping" by Barbara Liskov and Jeannette Wing. ACM Transactions on Programming Languages and Systems, vol. 16, no. 6, Nov. 1994, pp. 1811-1841.
или в более поздней работе: Barbara Liskov, Jeanette Wing: "Behavioural Subtyping Using Invariants and Constraints", July 1999, CMU-CS-99-156 MIT Lab
G>>В рассуждениях о варианте S<-R я виду речь о т.н. behavioural subtyping... очень надеюсь на то, что Вам знаком этот термин.
G>Я с самого начала давал именно поведенческое определение подтипа, очень странно, что вы этого не заметили. Ну да ладно, давайте сюда ваше определение "behavioral subtyping". И давайте посмотрим, каким образом оно сделает вашу ошибку правильным решением.
Для начала указали бы мне на "ошибку"... а то я, по глупости своей, её так и не увидел. И уж тем более, я не увидел определения подтипа. Исключая, ту формуллировку ППЛ, что я привел в предыдущем посте. Если же Вы считаете ППЛ "поведенчиским" определением — то... ну я прям не знаю... Сама Лисков, в абстакт к "Behavioural Subtyping Using Invariants and Constraints" пишет что до сих пор им так и не удалось дать определения subtyping "based not only on type names and/or signatures, but also on a a description of the type's behaviour", и что в этой работе они сдвинулись с мертвой точки. Смешно...
Здравствуйте, Gaperton, Вы писали:
G>Во-первых, здесь нарушается все тот же LSP, так как подпихивание квадрата в программу, корректно работающую с прямоугольниками, может легко ее сломать.
Возникли две противоположные мысли:
— Конечно, если формально посмотреть, то LSP здесь нарушается (в соответствии с определением).
— С другой стороны, ужесточение предусловий — естественно для специализации. Хотя — спорный вопрос.
Например, TFileStream, открытый только на чтение, будет выбрасывать exception при попытке записи. Хотя, у базового TStream режимов (readOnly/readWrite) — нет. Повторюсь, вопрос спорный.
G>Так что так делать не стоит. Во-вторых, единственный корректный способ извратиться существует — это сделать метод SetBounds константным, возвращающим новый экземпляр с измененными границами. Надщо немного изменить ваш пример — вот так (C++):
G>
G>virtual Rectangle SetBounds(int width, int height) const;
G>
Вообще-то, нет абсолютно никакой разницы (с точки зрения LSP) между моим решением и твоим. Если, конечно, реализация Square::SetBounds осталась та же, т.е. с использованием throw.
Здравствуйте, gbear, Вы писали:
G>Неа...
G>Вариант 1. Ввести данную оп-цию в контракт квадрата. virtual Circle S.GetCircumcircle(). И перекрыть её в прямоугольнике.
С выбрасыванием исключения, потому что данная операция для прямоугольника неприменима. Хорошее решение, ничего не скажешь.
G>Вариант 2. Строим агрегат Circle A.GetGetCircumcircle(Square s). В этом случае перекрываем R.get_Size. Например, таким образом, чтобы R.Width = base.Size.
В результате получаем семантически непонятный результат. Еще хуже.
Тем не менее, для реализации вписаной окружности достаточно реализовать в прямоугольнике привидение его к "квадратному" виду. Т.е. если мы обращаетмся к R.GetInscribedCircle() то R.Width становится равно S.Size.
A>В результате получаем семантически непонятный результат. Еще хуже.
Можно по подробней... о том, что Вы понимаете под "семантически непонятный"?
Здравствуйте, gbear, Вы писали:
G>Тем не менее, для реализации вписаной окружности достаточно реализовать в прямоугольнике привидение его к "квадратному" виду. Т.е. если мы обращаетмся к R.GetInscribedCircle() то R.Width становится равно S.Size.
Т.е. делаем функционал, который в постановке задачи отсутствовал. Это называется архитектурной ошибкой.
A>>В результате получаем семантически непонятный результат. Еще хуже.
G>Можно по подробней... о том, что Вы понимаете под "семантически непонятный"?
В том, что смысл того функционала, о котором я говорил, применительно к пямоугольнику неясен.
G>>Если для любого o1 <- S найдется такой o2 <- T, что для любой программы p <- P, определенной в терминах T поведение p не изменится при замене o1 на o2, то S является подтипом T.
G>Ой спасибо А я-то, тупил...
Всегда пожалуйста.
G>Тем не менее, таки, попрошу дать определение хотя бы "поведение p не изменится". Если у нас программа рисовала квадраты... и, вдруг, начала рисовать прямоугольники — изминилось или нет её поведение? Почему?
Программа определена в терминах Т. Ее поведение не должно измениться именно в этих терминах. Это действительно немного путанный момент — можно мне переформулировать так, чтобы было понятнее?
Если для любого o1 <- S найдется такой o2 <- T, что не будет существовать такой программы p, которая при замене o1 на o2 способна определить эту замену, то S является подтипом T.
Насчет "определить замену" — на случай, если вы опять не поймете, поясню. Это означает, что вы не сможете написать программу, которая, пользуясь интерфейсом Т, могла бы понять, что ей подпихнули подтип этого Т — S.
Или, что то же самое, подтип не должен нарушать инвариантов типа.
В контексте этого разговора довольно забавно выглядят ваши слова.
И лучше бы перед тем как... почитать таки оригинал. Это поможет снять некоторые вопросы. И может быть Вам после этого станет ясно, почему, например, уже в определении ПЛВ нет этих терминов.
G>Насчет ссылок — а чем Вас библиографическая не устраивает?! Тем более, я сильно сомневаюсь, что это где-то ещё можно найти в открытом доступе.
В интернете дофига лекций на эту тему, можете не сомневаться. Библиографическая ссылка плоха тем, что по ней довольно сложно сходить немедленно. Лучше приводить гиперссылку. Например, статьи Лисков, на которые вы ссылаетесь ниже, лежат в открытом доступе. И эти статьи вам, кстати, ни капельки не помогут.
G>Вы меня удивляете ей богу... "Принцип Подстановки Лисков" Вам значит известен, а вот "Подтипы Лисков-Винг" (Liskov Wing Subtyping) значит нет?! Весело блин...
Я не обязан уметь расшифровывать ваши КГАМ, извините. Ваши авторские аббревеатуры в институтах пока не преподают, да если бы и преподавали — уверяю вас, для меня нет ничего стыдного или удивительного в том, чтобы сказать, что я что-то не знаю — это слишком легко и просто исправить.
Так что извольте не удивляться, что вас не понимают, а изъясняться нормальным человеческим языком. Если, конечно хотите, чтобы вас понимали.
G>> Ну да ладно, давайте сюда ваше определение "behavioral subtyping". И давайте посмотрим, каким образом оно сделает вашу ошибку правильным решением.
G> Для начала указали бы мне на "ошибку"... а то я, по глупости своей, её так и не увидел.
Уже указывали двадцать раз. Бесполезно, вы каждый раз уводите разговор в сторону. Вы думаете, что выглядите умнее, отказывавясь признавать свою ошибку? Вы не правы, все ровно наоборот.
G>И уж тем более, я не увидел определения подтипа. Исключая, ту формуллировку ППЛ, что я привел в предыдущем посте. http://www.rsdn.ru/Forum/Message.aspx?mid=1290517&only=1
Люди, знакомые с определением, его там увидели, несмотря на то, что я назвал это "наследованием". Ниже по ветке вы можете видеть дискуссию с Олегом на эту тему. Это, кстати, совершенно неважно, разговор не об этом.
Впрочем, я разговор заканчиваю, надоело смертельно. Всего доброго.
Здравствуйте, stalcer, Вы писали:
S>Например, TFileStream, открытый только на чтение, будет выбрасывать exception при попытке записи. Хотя, у базового TStream режимов (readOnly/readWrite) — нет. Повторюсь, вопрос спорный.
Кто говорит, что LSP никто ни при каких обстоятельствах не нарушает?
S>Вообще-то, нет абсолютно никакой разницы (с точки зрения LSP) между моим решением и твоим. Если, конечно, реализация Square::SetBounds осталась та же, т.е. с использованием throw.
Есть огромная разница. И она состоит в том, что реализация Square::SetBounds, естественно, другая, и возвращает прямоугольник или квадрат — что получится. Т.е. работает абсолютно корректно, только выглядит крайне глупо . По сути, это получился фабричный метод, которому в принципе не нужен доступ к состоянию объекта. Поэтому, строго говоря, этот метод вообще не является операцией над прямоугольником, он статический .
Но это не сложно поправить — замените его на пару методов SetWidth и SetHeight, пусть они возвращают объект правильного класса для каждого сочетания размеров.
Здравствуйте, Gaperton, Вы писали:
G>Кто говорит, что LSP никто ни при каких обстоятельствах не нарушает?
Да. Уж.
Что мы имеем:
1) ООП придумано, чтобы писать программы в терминах предметной области.
2) Принцип подстановки Лисков — один из принципов ООП.
3) С точки зрения нашей предметной области (математики) любой квадрат является прямоугольником.
4) Вся эта ветка форума показывает, что квадрат от прямоугольника наследовать нельзя, так как нарушается ППЛ.
Налицо явное противоречие. Значит один из пунктов неверный.
Подозреваю, нас злостно обманывали в школе и это пункт 3.
На самом деле, в математиеских изысканиях, мы оперируем понятиями точно также, как объектами в программе. Например, в ходе логического доказательства теоремы мы также "читаем" свойства понятий и также изменяем их. И я думаю, что вряд ли найдется математическое рассуждение, которое бы относилось полиморфно к прямоугольникам и квадратам в плане изменения длин их сторон. Так что, в математике квадрат является прямоугольником тоже только на "чтение" .
Здравствуйте, Gaperton, Вы писали:
G>Здравствуйте, stalcer, Вы писали:
S>>Например, TFileStream, открытый только на чтение, будет выбрасывать exception при попытке записи. Хотя, у базового TStream режимов (readOnly/readWrite) — нет. Повторюсь, вопрос спорный.
G>Кто говорит, что LSP никто ни при каких обстоятельствах не нарушает?
Имхо, вот так LSP не нарушется:
class Stream
{
public abstract bool IsReadonly();
public abstract void Read(...);
public abstract void Write(...);
}
class FileReadStream
{
public override bool IsReadonly() { return true; }
public override void Read(...) { ... }
public override void Write(...) { throw ...; }
}
Убери функцию IsReadOnly из класса Stream, и LSP уже нарушается. Такой же трюк можно и с квадратом проделать. И опять же на практике применяемая фишка.
stalcer,
G>> Во-первых, здесь нарушается все тот же LSP, так как подпихивание G>> квадрата в программу, корректно работающую с прямоугольниками, может G>> легко ее сломать.
s> Возникли две противоположные мысли: s> — Конечно, если формально посмотреть, то LSP здесь нарушается (в s> соответствии с определением).
И по сути тоже: если добавление подобного наследника может нарушить
соответствие существующего модуля своей спецификации, то, очевидно, что
таким действием мы делаем The Bad Thing.
s> — С другой стороны, ужесточение предусловий — естественно для s> специализации.
Это просто означает, что "специализация" в таком понимании не связана с
отношением is-a.
s> Например, TFileStream, открытый только на чтение, будет выбрасывать s> exception при попытке записи. Хотя, у базового TStream режимов s> (readOnly/readWrite) — нет.
Если спецификация соответствующих методов TFileStream изначально говорила
о возможности соответствующих исключений, то подобное наследование LSP не
нарушает.
G>>
G>> virtual Rectangle SetBounds(int width, int height) const;
G>>
s> Вообще-то, нет абсолютно никакой разницы (с точки зрения LSP) между моим s> решением и твоим. Если, конечно, реализация Square::SetBounds осталась s> та же, т.е. с использованием throw.
Ну, здесь, вроде бы, очевидно, что в таком виде функция даже виртуальной не
должна быть, не говоря уже об исключениях.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Gaperton, Вы писали:
G>>Здравствуйте, stalcer, Вы писали:
S>>>Например, TFileStream, открытый только на чтение, будет выбрасывать exception при попытке записи. Хотя, у базового TStream режимов (readOnly/readWrite) — нет. Повторюсь, вопрос спорный.
G>>Кто говорит, что LSP никто ни при каких обстоятельствах не нарушает?
S>Имхо, вот так LSP не нарушется:
S>Убери функцию IsReadOnly из класса Stream, и LSP уже нарушается. Такой же трюк можно и с квадратом проделать. И опять же на практике применяемая фишка.
Ага, а еще можно так (обобщая):
class Base {
public abstract int GetType();
public abstract void DoSomethingSpecialForDerived1();
public abstract void DoSomethingSpecialForDerived2();
public abstract void DoSomethingSpecialForDerivedN();
}
class Derived1: Base {
public override int GetType() { return 1; }
public override void DoSomethingSpecialForDerived1() { ... }
public override void DoSomethingSpecialForDerived2() { throw ... }
public override void DoSomethingSpecialForDerivedN() { throw ... }
}
class Derived2: Base {
public override int GetType() { return 2; }
public override void DoSomethingSpecialForDerived1() { throw ... }
public override void DoSomethingSpecialForDerived2() { ... }
public override void DoSomethingSpecialForDerivedN() { throw ... }
}
class DerivedN: Base {
public override int GetType() { return N; }
public override void DoSomethingSpecialForDerived1() { throw ... }
public override void DoSomethingSpecialForDerived2() { throw ... }
public override void DoSomethingSpecialForDerivedN() { ... }
}
Gaperton,
G>> Тем не менее, таки, попрошу дать определение хотя бы "поведение p не G>> изменится". Если у нас программа рисовала квадраты... и, вдруг, начала G>> рисовать прямоугольники — изминилось или нет её поведение? Почему?
G> Программа определена в терминах Т. Ее поведение не должно измениться G> именно в этих терминах. Это действительно немного путанный момент - G> можно мне переформулировать так, чтобы было понятнее?
G> Если для любого o1 <- S найдется такой o2 <- T, что не будет
G> существовать такой программы p, которая при замене o1 на o2 способна
G> определить эту замену, то S является подтипом T.
Полагаю, более практично говорить соответствии программы своей спецификации,
при условии, что программа корректна, т.е. учитывает все возможные исходы
использования T, заданные его спецификацией. Говоря о поведении, Барбара
Лисков как раз говорит о спецификации и соответствии требованиям.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
stalcer,
s> 1) ООП придумано, чтобы писать программы в терминах предметной области. s> 2) Принцип подстановки Лисков — один из принципов ООП. s> 3) С точки зрения нашей предметной области (математики) любой квадрат является прямоугольником. s> 4) Вся эта ветка форума показывает, что квадрат от прямоугольника наследовать нельзя, так как нарушается ППЛ.
s> Подозреваю, нас злостно обманывали в школе и это пункт 3.
В этом рассуждении три слабых места:
1) (Обычно) программа пишется не для математических доказательств,
соответственно, математика предметной областью не является.
2) Математические понятия квадрата и прямоугольника не предполагают от них
никакого поведения, соответственно, эти понятия в программе, наделяющей
соответствующие объекты поведением, могут использоваться очень ограниченно.
3) И да, понятия в программировании далеко не обязательно один-в один
соответствуют понятиям предметной области.
s> На самом деле, в математиеских изысканиях, мы оперируем понятиями точно s> также, как объектами в программе. Например, в ходе логического s> доказательства теоремы мы также "читаем" свойства понятий и также s> изменяем их.
Относительно изменения есть серьезные сомнения...
s> И я думаю, что вряд ли найдется математическое рассуждение, которое бы s> относилось полиморфно к прямоугольникам и квадратам в плане изменения s> длин их сторон. Так что, в математике квадрат является прямоугольником s> тоже только на "чтение" .
+1
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, dshe, Вы писали:
D>Ага, а еще можно так (обобщая): D>
D>class Base {
D> public abstract int GetType();
D> public abstract void DoSomethingSpecialForDerived1();
D> public abstract void DoSomethingSpecialForDerived2();
D> public abstract void DoSomethingSpecialForDerivedN();
D>}
D>
Ну, я же не писал IsFileReadStream(), а написал IsReadOnly(). В таком замаскированном виде смотрится куда приятней .
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>В этом рассуждении три слабых места:
ПК>1) (Обычно) программа пишется не для математических доказательств, ПК>соответственно, математика предметной областью не является.
Обычно — да, но просто где-то в этой ветке упоминается именно в таком контексте.
ПК>2) Математические понятия квадрата и прямоугольника не предполагают от них ПК>никакого поведения, соответственно, эти понятия в программе, наделяющей ПК>соответствующие объекты поведением, могут использоваться очень ограниченно.
Очень даже предполагают, имхо. Они задают инварианты, которые определяют, в какой степени можно менять свойства экземпляра того или иного понятия.
ПК>3) И да, понятия в программировании далеко не обязательно один-в один ПК>соответствуют понятиям предметной области.
Но, ведь проблема-то как раз в том, чтобы найти способ как можно более похожего описания.
s>> На самом деле, в математиеских изысканиях, мы оперируем понятиями точно s>> также, как объектами в программе. Например, в ходе логического s>> доказательства теоремы мы также "читаем" свойства понятий и также s>> изменяем их.
ПК>Относительно изменения есть серьезные сомнения...
Если их нигде не изменять (как минимум — задавать), то зачем же их вообще читать?
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Gaperton,
G>>> Тем не менее, таки, попрошу дать определение хотя бы "поведение p не G>>> изменится". Если у нас программа рисовала квадраты... и, вдруг, начала G>>> рисовать прямоугольники — изминилось или нет её поведение? Почему?
G>> Программа определена в терминах Т. Ее поведение не должно измениться G>> именно в этих терминах. Это действительно немного путанный момент - G>> можно мне переформулировать так, чтобы было понятнее?
G>> Если для любого o1 <- S найдется такой o2 <- T, что не будет
G>> существовать такой программы p, которая при замене o1 на o2 способна
G>> определить эту замену, то S является подтипом T.
ПК>Полагаю, более практично говорить соответствии программы своей спецификации, ПК>при условии, что программа корректна, т.е. учитывает все возможные исходы ПК>использования T, заданные его спецификацией. Говоря о поведении, Барбара ПК>Лисков как раз говорит о спецификации и соответствии требованиям.
На мой взгляд это как раз менее практично в данном случае, так как
1) Позволяет развести спекуляции на тему "корректности" или некоректности поведения программ, и влияния разного поведения подтипов на спецификацию. Понятное дело, что спецификация составлена в терминах Т, но эти спекуляции совершенно к делу не относятся. Принимая это во внимание, я бы сказал, такое определение верно, но контринтуитивно.
2) Это определение не конструктивно в том смысле, что не дает прямого способа ("алгоритма") проверить или опровергнуть соответствие ему.
3) Введение понятий специфицкации и корректности в определение можно избежать. Бритва Оккама.