LINUX.ORG.RU

Вопрос по принципу Лисков.

 ,


0

3

Возьмем классический пример, когда принцип подстановки Лисков не может быть реализован. Точней так считает большинство. Пример квадрата как подкласса прямоугольника.

Основной вопрос тут заключается в том, может ли класс-клиент использовать интерфейс квадрата и прямоугольника единообразно.

Контракт прямоугольника (инвариант): ширина и высота положительны.

Контракт квадрата (инвариант): ширина и высота положительны; ширина и высота равны.

допустим, мы переопределяем методы квадрата setWidth и setHeight таким образом, что квадрат не реагирует на них, просто игнорирует.

Нарушили ли мы контракты? Нет. Нарушили ли мы принцип Лисков? Да, вроде, тоже нет. Тогда в чем проблема?

Ответ на: комментарий от den73

Ты вообще математику в школе учил?

Да. А ты?

В курсе, что такое «доказательство от противного»?

Да. Попробуй сформулировать утверждение, которое ты доказывал.

Я постулирую, что принцип Лисков безполезен, и я это доказал.

Этого ты не доказал. Ты доказал, что твое определение q(x) бесполезно.

А впутался я в это затем, что пытаюсь придумать ООП для Яр

А... ну тогда ладно, главное - чтобы тебе нравилось.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 3)
Ответ на: комментарий от tailgunner

Да. Попробуй сформулировать утверждение, которое ты доказывал.

Я могу. А вот теперь вопрос «зачем» уже возникает вовсю. Неужели приведённые мной примеры недостаточно вопиющи?

den73 ★★★★★
()
Ответ на: комментарий от den73

А вот теперь вопрос «зачем» уже возникает вовсю

ты хоть и хвастаешься математическим бекграундом, твои рассуждения скорей аутичны, нежели математичны. Невозможно доказать то что не определено. Чтобы что то доказывать ты должен иметь аксиоматику.

linearisation
() автор топика
Ответ на: комментарий от den73

Попробуй сформулировать утверждение, которое ты доказывал.

Я могу

Сделай это.

Неужели приведённые мной примеры недостаточно вопиющи?

Определение q(x) тупо бесполезно, поэтому пример не то, чтобы не вопиющ - вообще нерелевантен.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ты доказал, что твое определение q(x) бесполезно.

У меня нет никакого своего определения. Я взял его из Википедии:

Subtype Requirement: Let ϕ ( x ) be a property provable about objects x of type T. Then ϕ ( y ) should be true for objects y of type S where S is a subtype of T.

Сейчас я смотрю первоисточник, чтобы понять, что они понимают под этим. Но в статьях, например, на Хабре, это не расшифровывается и о необходимости сузить ϕ речи не идёт. Партия сказала «надо» - комсомол ответил «есть». Победа ученика - победа школы. Поражение ученика - поражение ученика. Поражает, что программирование всё же находится рядом с математикой, а мысли всё равно спускают людям сверху.

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 2)
Ответ на: комментарий от den73

should be true for objects y

Ни на что не намекает? Имя твоего ребенка == *отличное от имени родителя* верно для ребенка? Где тут противоречие?

linearisation
() автор топика
Ответ на: комментарий от den73

Ты доказал, что твое определение q(x) бесполезно.

У меня нет никакого своего определения

Как нет?

den73> q(x) формулируется как «результат М(x) совпадает с <а здесь мы подставляем тело от M>»

Вот именно «подстановка тела» и делает определение q(x) бесполезным. q(x) нужно формулировать в терминах возвращаемого значения и/или побочных эффектов. И да, выбор хороших q(x) - это проектирование программы. Принцип Лисков можно применять только для отсеивания плохих.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

What is wanted here is something like the following substitution property [6] If for each object o 1 of type S there is an object o 2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o 1 is substituted for o 2, then S is a subtype of T.

Вот что написано в первоисточнике. Из этого определение не следует нерелевантность ни одного из моих примеров. Придумать программу, которая печатает имя класса, размер экземпляра или вызывает виртуальный метод, предоставляется читателю.

Более того, виртуальные методы по этому определению вовсе запрещены (потомок _не_может быть подтипом в этом смысле, если виртуальный метод используется в программе по существу и как-то переопределяет предка. Даже переопределять абстрактный метод запрещено, поскольку поведение программы меняется).

И практический вывод отсюда - нет смысла вообще говорить о принципе Лисковой, он не нужен.

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от den73

Из этого определение не следует нерелевантность ни одного из моих примеров

Твои примеры (бессмысленные) не проходят проверку принципом Лисков. Всего лишь.

И практический вывод отсюда - нет смысла вообще говорить о принципе Лисковой, он не нужен.

Вывод другой - ты, лично ты, считаешь, что принцип Лисков не нужен. Это твое право, естественно - принцип Лисков не является строго доказанной теоремой.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Это твое право, естественно - принцип Лисков не является строго доказанной теоремой.

Он вообще теоремой не является. Теорема или гипотеза могла бы быть сформулирована в какой-то такой вот форме: «соблюдать принцип лисков при проектировании лучше(по таким то и таким то критериям), чем не соблюдать». А тут всего лишь декларация, которая вобще ни на чем не основывается, просто кому то так нравится. Ванечке нравится ковыряться в носу, поэтому должен соблюдаться принцип Ванечки — вот в таком духе. Собрались 2 аута в треде, и трут непонятно о чем.

linearisation
() автор топика
Ответ на: комментарий от tailgunner

Вот именно «подстановка тела» и делает определение q(x) бесполезным.

На самом деле первоисточник вот Я неоднозначно написал, исправляюсь. В Яре нет ООП, но я напишу, как будто оно есть:

о.класс Родитель ()
  метод М() -- целое виртуальный да
кнк
  
о.класс Ребёнок (Родитель)
  метод М() -- целое перекрытый-виртуальный да
кнк

о.метод Родитель.М() -- целое виртуальный да
  возврат 0
кнм

о.метод Ребёнок.М() -- целое перекрытый-виртуальный да
  возврат 1
кнм

выполнить-при-загрузке
  пусть Экземпляр = н.Родитель; // или н.Ребёнок
  пусть q -- да-нет = (: Экземпляр.М() == 0 :)
  печать q
кнв
Свойство q(x) - «метод М объекта x возвращает 0». Я даже не буду тебя спрашивать, релевантно ли это, я процитирую первоисточник, стр. 16:

First, properties of an object's behavior in a particular program must be preserved: to ensure that a program continues to work as expected, calls of methods made in the program that assume the object belongs to a supertype must have the same behavior when the object actually belongs to a subtype.

Теорию про инварианты и констрейнты я не читал, но в статье рассмотрено три примера «правильного» создания подтипа:

1. Добавление методов без добавления состояния

2. Сужение типа, т.е. вместо стека рассматривается стек глубиной 20.

3. Виртуальный базовый класс, у которого нет собственного поведения.

Т.е., типичная ситуация, когда Widget превращается в WidgetWithFrame путём наследования, скорее всего, не соответствует принципу Лисков.

Я теперь уже не так уверен, потому что понял, что в Википедии принцип сформулирован вне соответствия с первоисточником. В первоисточнике понятие о q сужено. Но это не значит, что ты (автор класса) можешь выбирать q. Множество возможных q автоматически следует из выбранного языка программирования и определения базового класса.

И так скажем, на 95% я уверен, что принцип Лисков не соответствует практике применения ООП.

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 5)
Ответ на: комментарий от den73

типичная ситуация, когда Widget превращается в WidgetWithFrame путём наследования, скорее всего, не соответствует принципу Лисков.

Еще один последний раз: принцип Лисков предназначен для оценки используемых контрактов, а не для оценки формы графа наследования.

Но это не значит, что ты (автор класса) можешь выбирать q. Множество возможных q автоматически следует из выбранного языка программирования и определения базового класса.

Если определение базового класса делаю не я - естественно, его контракт задаю не я. Мой производный класс должен всего лишь выполнить этот контракт.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Еще один последний раз: принцип Лисков предназначен для оценки используемых контрактов, а не для оценки формы графа наследования.

Речь не о форме, а о вирт.ф-ях. Виртуальные ф-ии, определённые в базовом классе и перекрытые в наследниках, скорее всего, нарушают принцип Лисков. Собственно, о том и пример про прямоугольник и квадрат. На практике желание пронаследовать квадрат от прямоугольника вполне объяснимо, а оказывается, принцип Лисков при этом нарушается. Думаю, навязывание этого принципа является формой конкурентной борьбы: нужно задать такие правила использования инструментов, чтобы в реальности их использовать было невозможно. Как написано в книжке «Анастасия», жрецы долго думали, как ускорить свою мысль, но у них ничего не вышло. Тогда они решили замедлить мысль всех остальных людей и получить преимущество хотя бы так.

Но я не понял: тебе это всё равно, ты с этим несогласен или что.

Например, QWidget имеет метод QWidget::heightForWidth(int w), который переопределён в его потомках. Значит, Qt не соблюдает принцип Лисков.

den73 ★★★★★
()
Ответ на: комментарий от den73

Еще один последний раз: принцип Лисков предназначен для оценки используемых контрактов, а не для оценки формы графа наследования.

Речь не о форме, а о вирт.ф-ях.

Речь о вирт.ф-ях ведешь только ты. Принцип Лисков говорит о контрактах классов в иерархии.

Думаю, навязывание этого принципа является формой конкурентной борьбы: нужно задать такие правила использования инструментов, чтобы в реальности их использовать было невозможно.

facepalm.jpg

Но я не понял: тебе это всё равно, ты с этим несогласен или что.

С чем «этим» - с книжкой «Анастасия»? Я не знаком с ней и не собираюсь знакомиться. Или с твоим пониманием принципа Лисков? Ты вообще говоришь о чем-то, что к нему не относится.

Например, QWidget имеет метод QWidget::heightForWidth(int w), который переопределён в его потомках. Значит, Qt не соблюдает принцип Лисков.

Возможно. Расскажи, какой контракт здесь нарушается.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Возможно. Расскажи, какой контракт здесь нарушается.

Вопрос упирается в вопрос «каков контракт QWidget». Если мы считаем контрактом документацию, то ничего, кроме сигнатуры и виртуальности, не определено. Если мы говорим о поведении программ, то реальное поведение у QWidget есть, программу, которая его использует, составить можно и принцип будет нарушен.

Здесь выясняется ещё один нюанс, что в оригинале говорится о _доказуемых_ свойствах, а в википедии - о _верных_ свойствах. Вывод отсюда таков, что не надо читать википедию, конечно же. Если свойства доказуемы, то можно говорить о «контракте» (о документации плюс интерфейсе) и о реализации (о поведении реальных программ). И при изучении программ можно говорить о разной аксиоматике: либо мы доказываем на основе документации, либо на основе реализации.

В реальной жизни программы обычно проверяются не формальными доказательствами, а тестами, поэтому с практической точки зрения, если класс QWidget можно воплотить в экземпляр, то принцип Лисков здесь нарушен.

den73 ★★★★★
()
Ответ на: комментарий от den73

Расскажи, какой контракт здесь нарушается.

Вопрос упирается в вопрос «каков контракт QWidget»

Ты так уверенно говорил о нарушении, что я было подумал - ты знаешь этот контракт.

Если мы считаем контрактом документацию, то ничего, кроме сигнатуры и виртуальности, не определено.

Итак, этот контракт не нарушен...

Если мы говорим о поведении программ, то реальное поведение у QWidget есть, программу, которая его использует, составить можно и принцип будет нарушен.

...но ты можешь придумать такой контракт, который невозможно не нарушить. Окей. Ты из этого пришел к ненужности принципа Лисков?

В реальной жизни программы обычно проверяются не формальными доказательствами, а тестами, поэтому с практической точки зрения, если класс QWidget можно воплотить в экземпляр, то принцип Лисков здесь нарушен.

Нет. Если контракт - это тест, а тест успешно выполняется и на QWidget, и на его потомке, то контракт не нарушен.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

ты знаешь этот контракт.

Его нельзя «знать». Это вопрос соглашения, я тебе предложил два варианта. А ты вместо того, чтобы начать разговор о том, что считать контрактом, пытаешься вывести меня на признание ошибки.

Да, если тебе это надо - я ошибся в том, что поверил Википедии. Её не надо было читать. И я говорил не о ненужности, а о парадоксальности (некорректности), которая есть в Википедии, но которой нет в первоисточнике. Конечно, парадоксальный принцип не нужен.

Ладно, он не парадоксален. Вопрос нужности остаётся. С одной стороны, вроде всё круто.

Но у меня получается, что если виртуальная функция является частью контракта, то она должна быть в базовом классе абстрактной (и на самом деле быть исключена из контракта) и по любой цепочке наследования должна переопределяться не более одного раза. Здесь получается иной парадокс: «опа есть, а слова нет». Т.е. мы заводим виртуальную функцию (скажем, draw) для полиморфизма, но контракта нет и пользоваться в базовом классе, не нарушив принцип Лисков, ей как бы нельзя. Но в реальности, именно этой вирт.ф-ей и надо пользоваться, и именно для базового класса всех виджетов, чтобы фактически нарисовать какой-нибудь грид виджетов.

В Qt я полез в примера подобной конструкции, но за 10 минут ничего лучше, чем QWidget::heightForWidth не нашёл. Думаю, принцип Лисков всё же тут нарушается, потому что нужно было бы написать «не вызывайте heightForWidght для QWidget». И естественно, компилятор бы не мог это проверить, что противоречит практичности.

Данного примера недостаточно, чтобы понять, применяется ли в Qt принцип Лисков. Мне всегда казалось, что в библиотеке виджетов есть базовый виртуальный Draw, который ничего не делает, и он несколько раз перегружается по иерархии. Т.е. никаким принципом Лисков и не пахнет. Как раз наоборот, виртуальность используется для того, чтобы рисовать разные виджеты по-разному, не зная конкретного типа виджета. Конечно, можно организовать это и по-другому. Но что-то мне сдаётся, что очень часто конкретный виджет рисуется не так, как его предок, который зачастую тоже может рисоваться.

Ладно, спасибо за беседу. Нужно будет потом ещё помедитировать над этим принципом. Он хорош тем, что определяет вменяемое понятие «подтипа» и в принципе позволяет частично обобщить понятие «подтипа» с базовых типов на объекты. Но вообще говоря, наследник в смысле ООП не является подтипом. И для разработки языка остаётся вопрос, как работать с «наследником в смысле ООП», как его назвать и нужно ли вообще такое понятие, как «наследник в смысле ООП».

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 2)
Ответ на: комментарий от den73

Данного примера недостаточно, чтобы понять, применяется ли в Qt принцип Лисков

Принцип Лисков - это правило проектирования (точнее, оценки результатов проектирования). Он не может «применяться в Qt» - он может либо применяться при проектировании Qt, либо соблюдаться (или не соблюдаться) в готовой библиотеке.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ну и зачем ты это написал? Понятно же, что мы не видим процесса проектирования Qt, а видим только результат, значит, понятно из контекста, о чём я говорю. С целью придраться к словам? Хорошо, пусть будет «соблюдается в Qt». Похоже, что «не соблюдается». И возникает вопрос: в каких практически значимых программах на любом языке он действительно соблюдается?

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 4)
Ответ на: комментарий от den73

Но это не к тебе вопрос, это вопрос из области общей философии. Тебе спасибо, тема в общем-то для меня на данный момент отложена, т.к. я занимаюсь другими вопросами.

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от den73

В общем, мой окончательный вывод:

Принцип Лисков разумен и имеет смысл. Но он имеет мало отношения к практике ООП. Лисков проанализировала то, что легко подогнать под теорию (типичное поведение теоретика). Есть анекдот про математиков на воздушном шаре. Нормальная практика ООП (метод draw) не подходит под принцип Лисков. Но это значит не то, что данная практика порочна, а то, что теория недостаточно развита. Соответственно, применять этот принцип в практике программирования, вообще говоря, не нужно. Если есть ясное понимание, как этот принцип полезен в конкретном проекте - можно им руководствоваться. Но общие слова о «хорошей архитектуре» не могут быть обоснованием.

С точки зрения языка, наследование в ООП иногда действительно создаёт подтипы и неплохо бы, чтобы компилятор это понимал. Но, как правило, оно создаёт новые типы, не являющиеся подтипами родителя. В частности, любая иерархия виджетов с виртуальным draw не соблюдают принцип Лисков.

Но проблема тем не менее есть: наследники обычно не являются подтипами своих предков.

Вот это

>(defclass Родитель () ())
#<STANDARD-CLASS COMMON-LISP-USER::Родитель>
>(defclass Ребёнок (Родитель) ())
#<STANDARD-CLASS COMMON-LISP-USER::Ребёнок>
>(subtypep 'Ребёнок 'Родитель)
T
T
можно расценивать как ошибку дизайна Common Lisp, если мы принимаем принцип Лисков для подтипов. Для выражения наследования нужно _ещё_одно_ отношение, не subtype.

Этим путём я и собираюсь следовать, если в Яре будет ООП в собственном смысле слова.

Критика приветствуется.

den73 ★★★★★
()
Ответ на: комментарий от den73

И так скажем, на 95% я уверен, что принцип Лисков не соответствует практике применения ООП.

Именно потому он и был сформулирован. Чтобы начали писать программы, в соответствии с ним, а не абы как.

anonymous
()
Ответ на: комментарий от den73

Но это значит не то, что данная практика порочна

В данном случае - именно это и значит. Нарушение принципа Лисков - первый звоночек на тему «вы готовите ООП не правильно!».

В частности, любая иерархия виджетов с виртуальным draw не соблюдают принцип Лисков.

соблюдает, конечно. С чего бы нет?

anonymous
()
Ответ на: комментарий от anonymous

SOLID или принцип Лисков? В любом случае, хотелось бы увидеть линк.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Нарушение принципа Лисков - первый звоночек на тему «вы готовите ООП не правильно!».

Это лишь чьё-то мнение.

соблюдает, конечно. С чего бы нет?

С того бы.

den73 ★★★★★
()
Ответ на: комментарий от den73

Уточнение: это верно в том случае, если существует вызов widget.draw, к-рый в реальности вызывает childWidget.draw и, таким образом, draw де-факто входит в контракт widget (независимо от того, что написано в проекте, есть де-факто используемый интерфейс widget).

Также это верно, если по какой-то цепочке наследования draw перекрыт более чем один раз.

den73 ★★★★★
()
Ответ на: комментарий от den73

Также это верно, если по какой-то цепочке наследования draw перекрыт более чем один раз.

это какая то необычная интерпритация принципа лисков. принцип в сущности то прост. на примере квадрата как подтипа прямоугольника всё просто:

У нас есть метод checkInvariants который принимает прямоугольник

void checkInvariants(Pramougolnik pr) {
  pr.setWidth(15)
  pr.setHeight(20)
  return pr.getWidth() == 15 && pr.getHeight() == 20
}


затем, в этот метод передаётся либо квадрат либо прямоугольник.

так вот для любого прямоугольника метод будет возвращать истину. а для квадрата ложь. Именно это и имеется ввиду когда говорят о принципе лисков. классы ведут себя по разному, инварианты нарушаются. То что вы с анонимусом можете задавать инварианты как угодно ежу впринципе понятно. Но вот чего вы никак не поймёте, так это то что _ожидаемое_ поведение для обычного класса в обычной ситуации - это посавить видтх в 15 и получить обратно 15. то же и с высотой. И нормальный программист будет ожидать нормального поведения когда будет работать с классом. а оно чёртас два нормальное. установил ширину в 15, потом никого не трогая установил высоту, а высота поменялась. А у прямоугольника не менялась. Вот в этом то и нарушение принципа. Вы с анонимусом задрали безграмотностью. книжек что ли почитайте вумных.

АПД. ну и да, это лишь пример

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от AndreyKl

У принципа Лисков, кроме примеров, есть ещё и определение. Я про квадрат с прямоугольником вообще особо не говорил в этой теме.

Давай так: есть виджет «окружность», у него перекрыт виртуальный метод draw, так что он рисует, естественно, окружность.

Вопрос N1 - является ли частью «контракта» виджета «окружность» то, что он рисует окружность? Дурацкий вопрос, правда? Конечно, да, иначе зачем он ещё нужен?

Далее мы наследуем от виджета окружность и делаем виджет «круг», т.е. «окружность, заполненная внутри» - переопределяем виртуальный метод draw.

Далее мы пишем программу, рисующую окружность. Далее в ней заменяем виджет «окружность» виджетом «круг». И - вуаля! Мы по определению нарушили принцип Лисков, потому что поведение программы не должно было измениться от замены класса на подкласс (см. формулировку принципа Лисков).

Т.е., если мы хотим соблюдать принцип Лисков, то нельзя так наследовать. А теперь смотрим в реальный мир и что видим? Не такое ли наследование мы видим сплошь и рядом?

В чём проблема с моим рассуждением?

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 2)
Ответ на: комментарий от den73

В частности, любая иерархия виджетов с виртуальным draw не соблюдают принцип Лисков.

Ты так и не понял, что бессмысленно говорить о нарушении принципа Лисков в отрыве от контракта, который нарушается.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Ты так и не понял, что бессмысленно говорить о нарушении принципа Лисков в отрыве от контракта, который нарушается.

Ты неправ, я всё понял. На самом деле не в отрыве от контракта - контракт един, он либо нарушается, либо нет. В принципе Лисков ничего не говорится о дроблении контракта на какие-то отдельные части. Ты можешь просто сказать своё мнение на тему, является ли draw частью контракта widget?

а) является

б) не является

в) как договоримся

г) иное

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 3)
Ответ на: комментарий от den73

P.S. насколько я понимаю, ты предлагаешь абстрагироваться от конкретной сигнатуры класса и рассматривать контракт абстрактно. Это выглядит довольно неуклюжей уловкой, но пусть даже так.

den73 ★★★★★
()
Ответ на: комментарий от den73

Далее мы пишем программу, рисующую окружность. Далее в ней заменяем виджет «окружность» виджетом «круг». И - вуаля! Мы по определению нарушили принцип Лисков, потому что поведение программы не должно было измениться от замены класса на подкласс (см. формулировку принципа Лисков).

По-моему бредовенько. Это ты сам выдумал или в книжке какой прочитал?

И да, на мой взгляд, конкретно в этом случае проблема с твоим рассуждением в том что метод draw обещает нам отрисовать объект так как положено отрисовывать объект. И именно это и будет ожидать любой нормальный человек. И это и есть контракт draw - отрисовать правильно объект. Так что ничего тут не нарушается.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 2)
Ответ на: комментарий от den73

в) как договоримся

Это.

насколько я понимаю, ты предлагаешь абстрагироваться от конкретной сигнатуры класса и рассматривать контракт абстрактно. Это выглядит довольно неуклюжей уловкой

Ты понимаешь неправильно и всё больше впадаешь в откровенный бред.

tailgunner ★★★★★
()
Ответ на: комментарий от AndreyKl

По-моему бредовенько.

Прекрасно, но это математика и это не аргумент.

И это и есть контракт draw - отрисовать правильно объект.

Не хватает только метода «сделать, чтобы было хорошо». Извини, но это не контракт. Контракт должен говорить, как конкретно прорисовать объект, а не «правильно».

den73 ★★★★★
()
Ответ на: комментарий от den73

Прекрасно, но это математика и это не аргумент.

слушай, ну ты нормальный?

вот есть виджет. у него есть дроу. что делает дроу у виджета? отрисовывает виджет, понимаешь?

дальше, окружность, наследник виджета. что делает дроу у окружности? отрисовывает виджет, который в данном случае окружность.

дальше, круг, наследник окружности, виджета. что делает дроу у круга? отрисовывает виджет, который в данном случае, сюрприз-сюрприз... КРУГ!!!!

ну и где нарушение?

хочешь, вырази на языке математики. всё там сойдётся, не волнуйся.

Не хватает только метода «сделать, чтобы было хорошо». Извини, но это не контракт. Контракт должен говорить, как конкретно прорисовать объект, а не «правильно».

и как же интересно задать контракт в таком случае для абстрактного объекта виджет?

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Слушай, ну если ты попытаешься договориться по этому конкретному пункту с любым вменяемым человеком, то не думаю, что у тебя что-то выйдет. Виджет как раз и нужен для того, чтобы рисоваться на экране, и естественно, без какого-либо описания того, что он изображает, нельзя сказать, что этот виджет имеет контракт вообще. Разве только в альтернативной вселенной, но мне туда не по пути.

den73 ★★★★★
()
Ответ на: комментарий от den73

Ты компилятор си на лиспе обещал написать, уже сделал или было некогда?

GoodRiddance
()
Ответ на: комментарий от AndreyKl

Ну вот, и ты туда же: т.е. неважно, что он отрисовывает, главное, чтобы что-то отрисовывал. А по принципу Лисков, если это важно, то все потомки должны отрисовывать то же самое, что и предок. Смотришь в википедию и подставляешь:

T - виджет окружность

S - виджет круг

q(x) - виджет рисует окружность

Обрати внимание, что q(x) ты не можешь выбирать, q(x) - это _произвольное_ доказуемое свойство, вытекающее из контракта класса.

Т.е. у тебя остаётся лишь один вариант: объявить вслед за Tailgunner, что неважно, что рисует виджет.

В этом случае представь себе, что ты заказчик и заказал мне библиотеку виджетов. Я тебе сдаю библиотеку, которая всегда рисует чёрный экран и говорю: ты не описал, как именно должны рисоваться виджеты, а значит, я выполнил задание - гони бабло.

Как только ты спохватишься и скажешь, что виджет окружность должен рисовать окружность, сразу окажется, что «рисовать окружность» является доказуемым (из контракта) свойством виджета окружность, и тогда виджет круг тоже должен рисовать окружность (подставь ещё раз в принцип Лисков). Пойми ещё раз: ты не имеешь права выбирать q(x), q(x) - это _любое_ свойство, вытекающее из контракта. Как там, для любого эпсилон существует дельта и так далее. Не тебе выбирать эпсилон.

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

Хорошо, что я от этого всегда умел уклоняться, авось и дальше пронесёт.

den73 ★★★★★
()
Ответ на: комментарий от AndreyKl

и как же интересно задать контракт в таком случае для абстрактного объекта виджет?

В случае абстрактного объекта в принципе можно отмазаться, сказав, что метод Draw есть, но он ничего никому не должен. Я недаром привёл именно такой пример, какой привёл - два конкретных объекта, наследующих друг от друга.

den73 ★★★★★
()
Ответ на: комментарий от den73

почему q(x) рисует окружность то?

в этом и момент, ты задаёшь контракт который выглядит странно. q(x) рисует виджет. и всё ок.

не вытекает из контракта что q(x) рисует окружность. чего оно у тебя вытекает? потому что контракт ты так составил. так в таком случае его что угодно нарушит. составь правильно и всё будет ок.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 2)
Ответ на: комментарий от den73

что с людьми определённых убеждений я вряд ли смог бы работать

ну за живое задел. что не так с убеждениями?

AndreyKl ★★★★★
()
Ответ на: комментарий от den73

т.е. вот гляди. если q(x) трактовать так как ты, то выходит что вообще наследовать нельзя. т.е. в итоге то мы наследуем для того чтобы изменить поведение. Т.е. наследующий класс будет обладать некоторыми другими доказуемыми свойствами чем наследуемый. Причём можно смело утверждать что это _всегда_ так (ну или приведи контрпример).

и если брать твою трактовку то наследование само по себе нарушает прицип лисков. но согласись это бредовенько.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от den73

и с математикой видишь, приятнее работать. сразу видно где ты ошибся. у тебя выходит то что принцип лисков указывающий как правильно использовать наследование, при этом запрещает само наследование. ну а это ерунда. чтд.

AndreyKl ★★★★★
()
Ответ на: комментарий от AndreyKl

то выходит что вообще наследовать нельзя. т.е. в итоге то мы наследуем для того чтобы изменить поведение.

Поздравляю - ты нарушаешь принцип Лисков.

и если брать твою трактовку то наследование само по себе нарушает прицип лисков. но согласись это бредовенько.

Нет, есть варианты наследования, которые его не нарушают. Но их очень мало. См. примеры в статье.

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от den73

Поздравляю - ты нарушаешь принцип Лисков.
Нет, есть варианты наследования, которые его не нарушают. См. примеры в статье.

ох ты блин, ладно. пойду прочитаю. потом если что посыплю голову пеплом.

AndreyKl ★★★★★
()
Ответ на: комментарий от AndreyKl

Вот тебе практически прямая цитата про круг и окружность:

to ensure that a program continues to work as expected, calls of methods made in the program that assume the object belongs to a supertype must have the same behavior when the object actually belongs to a subtype.

Ссылку уже давал на первой странице. Повторяю. http://reports-archive.adm.cs.cmu.edu/anon/1999/CMU-CS-99-156.pdf

den73 ★★★★★
()
Ответ на: комментарий от den73

ты знаешь, читал статью в английской вики, и во первых строках вот что прочёл

if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of T (correctness, task performed, etc.)

таки заметь *desirable* properties of T. так что шах и мат (если вики не врёт конечно).

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от den73

в том смысле что нужные свойства всё таки выбираем мы. что в общем-то весьма логично. строим программу, выбираем свойства.

я тебе сразу об этом сказал, контракт можно задать как угодно, это ежу понятно. вот только нормальные люди задают контракты нормально.

AndreyKl ★★★★★
()
Ответ на: комментарий от den73

to ensure that a program continues to work as expected, calls of methods made in the program that assume the object belongs to a supertype must have the same behavior when the object actually belongs to a subtype.

однако ты трактуешь очень широко. просто выбери _нужные_ _тебе_ свойства, и строй для них иерархию, и у тебя будет соблюдаться принцип.

если в примере про прямоугольник и квадрат ты уберёшь инварианты для видтх и хейгхт. то у тебя, как заметил анонимус, всё сойдётся. проблема только в том что обычный программист будет ожидать эти инварианты. т.е. люди ожидают нормальных контрактов а не дурацких. но в самом принципе нужные свойства выбираешь ты сам. а затем - если у тебя иерархия сохраняет выбранные свойства - то принцип не нарушен, не сохраняет - нарушен. дурацкие свойства выбрал, но они сохранились в иерархии (как анонимус предлагал тут)? ну что ж, принцип не нарушен, но ты сам дурак.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от den73

Ну и да, убеждения это концентрированное знание. В моём случае. А если у тебя убеждения, а основания у них хлипкие, так конечно, нафиг такие убеждения.

AndreyKl ★★★★★
()
Ответ на: комментарий от den73

С того бы.

Тебе же выше уже все объяснили. Если твое draw имеет контракт «чтото рисовать», то переопределенные draw для того, чтобы удовлетворять принципу Лисков, должны что-то рисовать, не более. Если контракт животного для ф-и «подергать за хвост» будет «издать звук», то потомки в виде кошки, которая мяукает, собаки, которая гавкает, свиньи, которая хрюкает - удовлетворяют принципу.

Того, чтобы метод вел себя абсолютно точно так же - никто не требует.

anonymous
()
Ответ на: комментарий от AndreyKl

В прошлой удаленной теме он рассказывал, как планирует провести вечер с поллитрой водки внутри и интересным разговором на ЛОР. Да и до того говорил, что постил пьяным.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от anonymous

то переопределенные draw для того, чтобы удовлетворять принципу Лисков

Я на это уже ответил: ты заказчик, я исполнитель. Я тебе сдаю библиотеку виджетов, которая рисует чёрные экраны. Я выполнил ТЗ, каждый виджет «что-то рисует». Подписывай акт и гони деньги.

den73 ★★★★★
()
Ответ на: комментарий от AndreyKl

если вики не врёт конечно

Врёт. Я так понял, ты статью не осилил?

контракт можно задать как угодно, это ежу понятно. вот только нормальные люди задают контракты нормально.

Именно! Я тебе приводил пример с заказчиком и чёрным экраном. Любой нормальный контракт должен содержать слова о том, что «виджет окружность рисует окружность», а «виджет круг рисует круг». Контракт весь целиком подпадает под принцип Лисков. А ты похоже, хочешь сказать «когда я пишу документ для заказчика, то это контракт, а когда применяю принцип Лисков, то это не контракт». В статье написано: любое (выбранное не тобой) свойство, доказуемое из спецификации. Если ты начинаешь _сам_ выбирать свойства, к которым применять принцип Лисков, то тогда принцип Лисков вообще ни к чему не обязывает: можно на время его применения сделать контракт пустым. И, соответственно, нет смысла обсуждать его. И главное, твои слова о его «применении» не гарантируют надёжность работы программы. Потому что из принципа имеется следствие: можно заменить одно на другое и _никакая_ программа не сломается. В твоём случае это не так.

На каком основании ты меняешь контракт во времени? Помнится, в книжке «Несвятые святые» повествуется о том, как заменили товарища в делегации, и вместо тёти за границу поехал дядя.

На границе его тормознули и он стал говорить: «Вообще-то я Иван Иванович, но в данном списке я - Ольга Алексеевна». В итоге он проехал через границу (это был довольно крутой церковный дядя).

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

То же замечание касается и tailgunner.

Ну и да, убеждения это концентрированное знание. В моём случае.

Ты переоцениваешь свои знания.

В общем, это ваши с ним проблемы, давайте не будем тратить время :) Спасибо за беседу.

den73 ★★★★★
()
Последнее исправление: den73 (всего исправлений: 2)
Ответ на: комментарий от den73

Абстрактный метод - это контракт на то, что клиент сможет вызвать данный метод. Когда ты перекроешь абстрактный метод, у тебя появится контракт на то, как ты его реализовал. Реализовал окружность - значит клиенты будут ожидать окружность. Где гарантия, что на каком-то уровне иерархии потомок окружности не нарисует радужного квадратного пони? В соблюдении SOLID. Если ты перекрыл окружность таким образом, что метод начал рисовать заполненную окружность, ты нарушил контракт. Это повод задуматься, правильно ли ты выстроил иерархию классов. Возможно, тебе нужна абстрактная окружность, потомки которой будут реализовывать пустую и заполненную окружности. Возможно, тебе нужен потомок окружности с примесью fill. Возможно, тебе нужно не наследование, а делегирование.

А вообще, это все условности. На практике ведь никто не держит перед собой чек лист с пунктами SOLID, делают по интуиции. К бест практис прибегают, когда нужно обосновать проектное решение или рефакторинг имеющегося решения.

winlook38 ★★
()
Ответ на: комментарий от winlook38

В соблюдении SOLID.

Ага, именно в соблюдении буквы L в данном случае.

Если ты перекрыл окружность таким образом, что метод начал рисовать заполненную окружность, ты нарушил контракт.

Спасибо за поддержку :) А то когда все вокруг мне пытаются объяснить, что 2х2=5, это как-то некомфортно.

Возможно, тебе нужно не наследование, а делегирование.

Угу.

А вообще, это все условности.

Во! Именно, кто-то прочитал про SOLID, но некоторые настолько по интуиции делают, что даже не поняли, в чём состоит L.

К бест практис прибегают, когда нужно обосновать проектное решение или рефакторинг имеющегося решения.

Отличное замечание! Как Ходжа Насреддин прибегал к астрологии, когда ему нужно было обосновать то или иное решение перед султаном.

den73 ★★★★★
()
Ответ на: комментарий от winlook38

Если ты перекрыл окружность таким образом, что метод начал рисовать заполненную окружность, ты нарушил контракт.

Только если контракт был на незаполненную окружность.

tailgunner ★★★★★
()
Ответ на: комментарий от den73

Ага, именно в соблюдении буквы L в данном случае.

Почему же, L - это всего лишь тема данного топика.

Спасибо за поддержку :) А то когда все вокруг мне пытаются объяснить, что 2х2=5, это как-то некомфортно.

А иначе я не знаю, как можно соблюсти L и S.

Как Ходжа Насреддин прибегал к астрологии, когда ему нужно было обосновать то или иное решение перед султаном.

Я не знаю, согласился ты или троллишь меня, но на практике упоминания о том или ином хорошем подходе я слышу в основном когда готовятся к рефакторингу, реже на этапе ревью. А разработчик по возможности сам придерживается хороших практик (интуитивно или сознательно).

winlook38 ★★
()
Ответ на: комментарий от tailgunner

Только если контракт был на незаполненную окружность.

Окружность всегда не заполнена, по определению. Но я вижу, ты начал понимать, что я прав - это хорошо.

den73 ★★★★★
()
Ответ на: комментарий от winlook38

Я согласился.

Думаю, что аналогия с Ходжой и астрологией реально имеет место при обосновании любых проектов, ибо те, кто платят деньги, далеко не всегда разбираются в сути проектов. Хотя к разным «методикам проектирования», «принципам» и «умным книжкам» я довольно скептически отношусь и это я тоже выразил :) Ну и плюс к тому обсуждение данного топика показывает, что у некоторых и в собственной голове астрология :)

Почему же, L - это всего лишь тема данного топика.

Пускай, не суть.

den73 ★★★★★
()
Ответ на: комментарий от tailgunner

Только если контракт был на незаполненную окружность.

Вот тут для меня не совсем ясный момент, из-за которого, как я понимаю, возникают основные разногласия. Я понимаю, что если мы что-то реализовали и сказали (условно, тестами или еще как-то), что гарантируем такое поведение, то перекрытие будет нарушением контракта. Если мы не заявили в чем контракт, то его и нет. Но это мне и кажется странным. Я плохо представляю себе ситуацию, когда реализованное поведение не является гарантированным (получается, реализация есть, а ответственности за нее нет). Точнее представляю, но это ограниченный перечень случаев, какое-то дефолтное поведение. Если можешь, приведи примеры, когда такая реализация будет оправданной. Я могу предположить, что это ассерты, в языках, в которых нет абстрактных методов, или минимальное дефолтное поведение, которое, нас устраивает, но скорее всего будет перекрыто в потомках.

Из своей практики могу сказать, что в нашей системе тоже не все гладко с соблюдением SOLID, но если посмотреть на перекрытые методы, то, я думаю, они очень просто рефакторятся во что-нибудь, соответствующее SOLID, без необходимости объявлять реализацию не покрытую контрактами.

winlook38 ★★
()
Ответ на: комментарий от den73

Я на это уже ответил: ты заказчик, я исполнитель. Я тебе сдаю библиотеку виджетов, которая рисует чёрные экраны. Я выполнил ТЗ, каждый виджет «что-то рисует». Подписывай акт и гони деньги.

А при чем тут ТЗ?

anonymous
()
Ответ на: комментарий от den73

Любой нормальный контракт должен содержать слова о том, что «виджет окружность рисует окружность», а «виджет круг рисует круг».

Ну так и в чем тут проблема того, что виджет круга является потомком виджета окружности? Вот у меня есть какойто виджет-окружность, контракт - нарисовать окружность (какую-то, любую), на его место приходит виджет круг, рисует круг (что является некоей окружностью), контракт выполнен, проблемы?

anonymous
()
Ответ на: комментарий от winlook38

Я понимаю, что если мы что-то реализовали и сказали (условно, тестами или еще как-то), что гарантируем такое поведение, то перекрытие будет нарушением контракта

Я правильно понял - любое перекрытие будет нарушением контракта? Если да, то почему?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

любое перекрытие будет нарушением контракта?

Расширение поведения может не нарушать контракта, согласен. Собственно, иначе, наверное, не было бы места букве O в SOLID.

Но я бы придерживался этого подхода по максимуму (если бы фанатично следовал этому принципу), т.к. в условиях отсутствия простого способа проверить контракт (который может быть в голове проектировщика) грань легко переступить. В любом случае, пример с окружностью и кругом мне не нравится, т.к. (не считая того, что мы не обозначили четкий контракт) его легко переработать, чтобы не возникало таких холиварных споров.

winlook38 ★★
()
Ответ на: комментарий от den73

Врёт. Я так понял, ты статью не осилил?

не пытался. полагаю, что там на самом деле ничего не говорится о способе наполнить T. но как будет выходной - погляжу статью. за ссылку спасибо.

AndreyKl ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.