Наследование, Circle vs Ellipse [из [java] Почему я не люблю Java]
Предложи лучше. Кстати, в python есть ещё реализация полиморфизма через switch, смотри PEAK.
От них ещё польза, что архитектуру делаешь не опираясь на иерархии, что обычно похоже на чесание уха левой пяткой, а просто учитывая свойства описанные в интерфейсах.
Т.е. язык "классов с наследованием" изначально был неудачной идеей: слишком много от реализации в неё включено.
И я не понял, как ты обеспечишь полиморфизм на интерфейсах. Тем же самым наследованием, но только интерфейсов?
Погоди, тебе не нравится полиморфизм через наследование или вообще наследование?Полиморфизм через наследование. С полиморфизмом через наследование плохо уже то, что производится неявное преобразование, от производного класса к одному из его предков.
Но это большо трындёжь. Претензия, на самом деле, одна: неумеренное использование этого инструмента и оттого размазанная на кучу файлов реализация интерфейса, тогда как при явном существовании сущности interface (trait, type class и т.п.) этого обычно нет, обычно всё локализовано в одном файле.
производится неявное преобразованиехаха. Зависит от реализации. Смотри питон. Там всё есть словарь. И имея объект всегда можно получить породивший его класс и всех родителей.
И имея объект всегда можно получить породивший его класс и всех родителей.Так это в любом языке, где RTTI есть можно сделать.
ну в питоне ООП - это вообще сахар.
хаха. Зависит от реализации. Смотри питон. Там всё есть словарь. И имея объект всегда можно получить породивший его класс и всех родителей.Замуты питоновского наследования - отличный повод избегать его использования.
Да и как-то в динамических языках это не столь актуально, если не пытаться сделать всё заебись и не наворотить нетестируемую архитектуру с лапшой-наследованием о десятке модулей, которую потом распутывать удерживая всё в уме.
Вот да. В динамических языках, где везде передаешь и хранишь в контейнерах какой хош тип, число ситуаций, когда надо использовать наследование, мне кажется, значительно сокращается.
Вот да. В динамических языках, где везде передаешь и хранишь в контейнерах какой хош тип, число ситуаций, когда надо использовать наследование, мне кажется, значительно сокращается.Наследование - это естественный способ упорядочивания объектов. Это вообще концепция, и не обязательно она должна быть реализована в языке программирования. Я её встречал и в каталогизаторах и в настроечных файлах. Неужели эта естественная концепция нуждается в дополнительных обоснованиях?
Неужели эта естественная концепция нуждается в дополнительных обоснованияхДа. Если применение наследования не продиктовано необходимостью, то за каким хреном его применять? Для красоты что ли?
Вообще, наследование - нихрена не естественная концепция для большинства случаев. Естественная концепция - это выполнение операций для определенного класса объектов, для которых выполняются определенные требования. Т.е. шаблоны фактически. А вот реализация требований к объектам через выстраивание иерархии - это в большинстве случаев нихрена не естественно и неудобно.
Наследование - это естественный способ упорядочивания объектов. Это вообще концепция, и не обязательно она должна быть реализована в языке программирования. Я её встречал и в каталогизаторах и в настроечных файлах. Неужели эта естественная концепция нуждается в дополнительных обоснованиях?Если б ты учил историю, знал бы что наследование гораздо менее естественно чем списки, соответственно и изобретено позже.
Да. Если применение наследования не продиктовано необходимостью, то за каким хреном его применять? Для красоты что ли?Для понятности и наглядности. Мне казалось, Карл Линней продемонстрировал со всей возможной наглядностью преимущества наследования для упорядочивания сложных информационных структур. С результатами его работы знакомы все школьники. Для любого представителя нашей культуры такой подход очевиден и стал естественным после многочисленных применений и в других отраслях знания.
Для понятности и наглядности.Ну давай, например, подумай, как ты реализовал бы с помощью наследования математические абстракции разные (ну там, поле, кольцо, группа, линейное пространство, проективное пространство и т.п.), операции с ними и конкретные их примеры (матрицы какие-нибудь, множество целых чисел). С наследованием это будет просто ужасная мешанина из абстрактных классов. С шаблонами же (или просто на динамическом языке, без наследования) будет все на порядок проще. Потому что они следуют естественной математической логике - мы задаем аксиомы, задаем множество операций, и можем работать со всеми объектами, которые удовлетворяют аксиомам и поддерживают данные операции. никаких непонятных иерархий.
Опять же, проводить аналогии между классификацией видов и программированием - это как-то очень круто. Возможность таких аналогий и границы их применимости надо еще доказывать.
Для понятности и наглядности. Мне казалось, Карл Линней продемонстрировал со всей возможной наглядностью преимущества наследования для упорядочивания сложных информационных структур. С результатами его работы знакомы все школьники. Для любого представителя нашей культуры такой подход очевиден и стал естественным после многочисленных применений и в других отраслях знания.Бедняга, ты даже не в курсе, что самые многочисленные, самые эволюционно активные животные на земле - бактерии и археи - не укладываются в твою иерархию от слова никак. Карлу Линнею было позволительно заблуждаться, но не знать об этом в 21 веке - позор.
Мы ценим его подход, а не исчерпывающий список, им так и не составленный
Вот пример области, где такой подход ни к чему хорошему не приведет, я уже привел - это математика. В программировании тоже не надо без необходимости какую-то иерархию классов городить.
я уже привел - это математика.Ага. Кольцо уже не расширяет группу.
Ну ты, говорю, подумай, как задать какие-нибудь конкретные объекты, которые удовлетворяют аксиомам разных классов объектов. Какая чудовищная мешаниа наследования получится.
Ага. Кольцо уже не расширяет группу.Ты ВМК заканчивал?
ну простой пример: окружность это подкласс эллипса, но требует меньше полей для своего описания
http://github.com/non/spire
Можешь полюбоваться.
держи: Можешь полюбоваться.
да, да, оно
есть ли языки, в которых наследование это допускает?
есть ли языки, в которых наследование это допускает?если идти с точки зрения контракта, то вообще нет проблем.
Circle(centre) extends Ellipse(centre, centre)
Если смотреть с точки зрения непосредственно экономии места/слотов - по идее ничего сложного. Нужен язык, который умеет инлайнить функции вместо полей. Я такого не знаю, но такую фичу добавить не сложно.
ну вообще окружность хотя математически подкласс эллипса, но к ней неприменимы некоторые операции с эллипсами, например вычислить большую полуось
если правильно делать, то попытка взять большую полуось окружности должна приводить к ошибке компиляции
Да вообще связь А -> B слишком примитивна, поэтому к типичному ООП языку сразу прилагается книга с паттернами, где учат как жить с костылями вместо ног.
Паттерны есть в любом языке.
можно выдавать произвольную ориентацию. Главное не менять её.
в биологии тоже потомок может быть устроен проще предка - потерять геныВ плюсах можно удалять методы.
есть ли языки, в которых наследование это допускает?
А что не так в его примере? Кольцо же является группой по сложению, не?
Кольцо же является группой по сложению, не?
абелевой группой же? а кто кого расширяет из пары группа - абелева группа?
Ну, абелева группа - это всё же группа, так что всё норм пока
т.е. "А является В <=> А расширяет В" ?
Тебе глагол не нравится что ли? Пример же был в контексте наследования в матеметике. Норм пример вроде. БОльшая часть утверждений про группу будет справедлива и для кольца
А что не так в его примере? Кольцо же является группой по сложению, не?А чем еще оно является? Очевидно что в математике никакой иерархии в удобном для наследования духе нет, есть наборы утверждений и следствия из них. Условно, "если верны эти 3 из 5 то А, если другие 3 из 5 то Б".
Для интересу наблюдения за корчами айвенги можно занять его построением какой-нибудь иерархии наследования для полугрупп, групп, колец и полуколец. Потом добавить туда подгруппы и подкольца. Потом идеалы и алгебры.
Для интересу наблюдения за корчами айвенги можно занять его построением какой-нибудь иерархии наследования для полугрупп, групп, колец и полуколец. Потом добавить туда подгруппы и подкольца. Потом идеалы и алгебры.Я уже дал ссылку на готовый продукт. Могу копипасть сюда по кускам, если вы гитхаб не освоили.
Я потому и говорю что ты с ВМК, а значит знаешь только одну математику, и не знаешь как она меняется при добавлении-удалении утверждений.
Я потому и говорю что ты с ВМК, а значит знаешь только одну математику, и не знаешь как она меняется при добавлении-удалении утверждений.От добавления новых ограничений, старые свойства не теряются. Сколько не накручивай дополнительных утверждений, пока ты пользуешься конъюнкцией, они выполняться будут все. Другое дело, описывать мир в стиле бесконечных дробей, строя каждое следующее утверждение на модификации предыдущего. Но вот беда, даже математикам такое представление не удобно для оперирования, и они возвращаются обратно к простому комбинированию свойств.
Вообще, жизнь, как предметная область, сложна, подчиняется множеству законов. Не всё хорошо ложится на концепцию наследования, например, иерархия жёсткого диска - это включение, а не наследование. Но наследование встречается очень часто, легко распознаётся сознанием, почему бы его не использовать столь же часто и естественно.
Скопировать тебе определение полугруппы с гитхаба? Потом можно будет посмотреть, как она расширяется новыми контрактами.
От добавления новых ограничений, старые свойства не теряются
есть как куча свойств (следствий из определяющих свойств), которые верны для "родителя" и не верны для "ребенка" так и наоборот
есть как куча свойств (следствий из определяющих свойств), которые верны для "родителя" и не верны для "ребенка" так и наоборотЯ что-то туплю. Можно пример?
- наследование контракта(интерфейса)
- наследование реализации
Наследование контракта используется для реализации полиморфизма и обязано подчиняться правилу Лисковой.
Наследование реализации используется для уменьшения дублирования кода/настроек и не требует выполнения правила Лисковой.
Круг vs Ellipse
Круг является вырожденным случаем эллипса.
Контракт круга не поддерживается полностью контрактом эллипса.
Контракт эллипса не поддерживается полностью контрактом круга.
Реализацию круга может быть реализована через наследование реализации эллипса.
Реализация эллипса может быть реализована через наследование реализации круга.
а круг, например, является фигурой с постоянным диаметром
ну если говорить про круг и эллипс, то у эллипса существует две различных точки, такие что сумма расстояний до них бла-бла-бла, а у круга их нетубери требование различности и будет тебе счастье. Назовём его нормализованным эллипсом. И в дальнейшем будем использовать только его. А потом можно сделать уже набор наследников, где точки различны, или где фокусы отстоят друг от друга на определённое расстояние.
а круг, например, является фигурой с постоянным диаметром
Контракт круга не поддерживается полностью контрактом эллипса.Где не поддерживается?
Контракт эллипса не поддерживается полностью контрактом круга.
Вообще, разделение принципа наследования (контракт) и конкретной реализации в языке программирования - это важное замечание. Потому что вовсе не обязательно наследование реализовывать через тупое копирование полей/методов, как это делает питон, сливая словари. Можно применять правила генерации наследуемого класса любой степени хитрожопости, лишь бы программисту было удобно и не нужно было повторно копипастить один и тот же код.
убери требование различности и будет тебе счастье.
это уже подгонка
Назовём его нормализованным эллипсом. И в дальнейшем будем использовать только его. А потом можно сделать уже набор наследников, где точки различны, или где фокусы отстоят друг от друга на определённое расстояние.
я не понял, что от чего ты наследовать в итоге собрался - круг от эллипса или наоборот?
С эллипсом и кругом не всё так просто. На read-интерфейс там в одну сторону можно наследовать, на write - в обратную. В строгом смысле они друг от друга не наследуются
очевидно, что круг - это частный случай эллипса. Для него выполняются все правила обобщённого эллипса. Понятное дело, что конкретный эллипс может не являться окружностью, например, если у него задан ненулевой эксцентриситет.
Где не поддерживается?например, в функции:
- возвращение осей симметрии. Circle: бесконечное множество, Ellipse: пара осей
С эллипсом и кругом не всё так просто. На read-интерфейс там в одну сторону можно наследовать, на write - в обратную. В строгом смысле они друг от друга не наследуютсяа что не так с write интерфейсом? У тебя метод writeFocals(a,b) возвращает объект типа Ellipse. Окружность просто не будет переписывать этот метод, будет возвращать точно также Ellipse.
Если ты хочешь чтобы write у круга возвращал круг, то тебе уже нужно определять Higher-order type и он уже будет инвариантен относительно реализации.
- возвращение осей симметрии. Circle: бесконечное множество, Ellipse: пара осейCircle возвращает любую пару осей. Выполняет контракт.
Circle возвращает любую пару осей. Выполняет контракт.Не соблюдает в следующем сценарии:
1. размещаем два эллипса под углом phi-градусов к горизонту
2. получаем оси симметрии фигур и проверяем что они сонаправлены
замена одного эллипса на круг сломает тест
ООП - хорошая абстракция для некоторых вещей, но не для всех.
ООП хорошая абстракция для математики, при понимании отличий наследования контракта от наследования реализации. И аккуратном использовании этих концепций.
Но можно построить другой пример - применения оператора вращения к фигуре. После вращения на пи должны получить разные оси, после второго вращения оси должны снова совпасть с оригиналом. Кругу для выполнения контракта придётся свою произвольно выбранную ось хранить и тоже вращать. А это уже натягивания совы на глобус.
У круга есть read-свойство радиус, у эллипса write-метод растянуть вдоль оси.
у круга тоже есть это свойство. Он тебе просто вернёт эллипс.
Тогда это не write-метод. Ты строишь immutable объект. В этом случае все круги эллипсы, да.
круг оказался не равен эллипсу. Оси тоже не равны. Ты точно так же мог взять два разных эллипса.Чего? Как этот ответ связан с тестом? В тесте не утверждалось, что эллипсы берутся идентичные. И не утверждалось, что оси идентичны.
Тогда это не write-метод. Ты строишь immutable объект. В этом случае все круги эллипсы, да.А ещё в питоне тоже. Там можно присваивать переменной не только значение, но и класс. Запросишь у круга растяжение, получишь на его месте эллипс. И это логично. Никто же не гарантирует, что наследованный от эллипса метод будет ограничен дополнительным инвариантом.
А как поступать, если ты эллипс до круга случайно ужал? Знать всё своё семейное древо?
ты имеешь в виду, что просто нельзя построить круг, наклонённый к горизонту под определённым углом? Ну так и нельзя провести круг через две различные фокальные точки. Контракт касается свойств фигуры, а не изготовления фигуры
очевидно, что круг - это частный случай эллипса. Для него выполняются все правила обобщённого эллипса. Понятное дело, что конкретный эллипс может не являться окружностью, например, если у него задан ненулевой эксцентриситет.до этого ты говорил про свойства, а не правила
и скажи уже для ясности что от чего наследуется
оставаться эллипсом. Контракт тебя не заставлять подбирать наиболее точного наследника.
Тогда это не write-метод. Ты строишь immutable объект. В этом случае все круги эллипсы, да.Это недостаток системы типов твоего языка, если нельзя окружность превратить в эллипс растягиванием.
кстати да. Вот возьмём ковариантный лист. Если B расширяет A и у нас есть List[B], то добавляя к нему элемент с типом A, мы получаем List[A]. Было бы хорошо, если на оператор присваивания и прочие мутабельные методы распространялся тот же принцип.
Круг наследуется от эллипса. В простейшем случае переопределяя конструктор, в более сложном - ещё переопределяя методы, заменяя их на более эффективную реализацию, учитывающую появившийся инвариант.
ты имеешь в виду, что просто нельзя построить круг, наклонённый к горизонту под определённым углом?Построить можно, но круг "не запоминает" направление построения в отличии от эллипса, и этим нарушает контракт.
в простейшем случае все круги возвращают одно и то же направление. Стабильность направления гарантируется
в простейшем случае все круги возвращают одно и то же направление. Стабильность направления гарантируетсяПо контракту необходимо то направление под которым строили, а не то которое выбрал круг.
что значит под которым строили? Контракт на объект не включает конструктора
Возможность наследования зависит не только от свойств объектов, но и от конкретных задач (контекста), в которых их планируется использовать. Где-то круг можно наследоват от элипса, где-то нет.
тут же айвенго пытается доказать, что наследование естественно во многих областях, в том числе в математике
Круг наследуется от эллипса.дай четкий критерий, по которому можно понять, что А наследуется от В
Контракт на объект не включает конструктораво-первых, это спорный тезис
во-вторых, указанный тест может быть проведен и без вызова конструктора
в простейшем случае все круги возвращают одно и то же направление. Стабильность направления гарантируетсяЭто уже костыль. Стабильность-то гарантируется, только смысловой нагрузки нет у этого метода.
всякий объекта класса А так же является объектом класса B. Вхождение множеств.
всякий объекта класса А так же является объектом класса B. Вхождение множеств.Откуда следует, что множество A является подмножеством множества B? И что A и B не находятся в отношениях пересечения, а не вхождения?
ни откуда. Если это соотношение выполняется, то можно использовать наследование. Если не выполняется - то бессмысленно.
Если это соотношение выполняется, то можно использовать наследование. Если не выполняется - то бессмысленно.На полном контракте соотношение никогда не выполняется. В полный контракт входит: как то чем объект является, так и то, чем объект не является. Если объект другого вида, то нарушается или первая часть, или вторая.
Соответственно, можно говорить лишь о соблюдении частичных контрактов. Наличие полиморфизма между объектами появляется только в рамках конкретной задачи. И он отсутствует - "вообще", безотносительно задачи.
. В полный контракт входит ... то, чем объект не являетсяА это вообще можно записать конечными количеством слов?
А это вообще можно записать конечными количеством слов?Всё запрещено, что не разрешено. (c)
Объект не является верблюдом. (c)
Полное: Всё не допустимо, кроме явно оговоренного в контракте.
Частичное: Объект не является X1. Объект не удовлетворяет условию Y1.
Объект не удовлетворяет условию Y1.Ты прав, от такого вообще наследоваться нельзя. Но ведь можно построить отрицание. Объект не является мутабельным => объект является иммутабельным. В языке программирования такого рода констрейты задать крайней сложно, но в общем языке вполне возможно. То бишь естественность наследования сохраняется, хотя и не передаётся на выразительные средства языков программирования.
т.е. целые числа наследуются от обыкновенных дробей? хотя последние определяютс через первые?
Но ведь можно построить отрицание. Объект не является мутабельным => объект является иммутабельным.Программирование, из-за наличия процедуры конструирования, работает в конструктивной логике, а не в common логике.
Соответственно, в конструктивной логике тезис "Объект не является мутабельным => объект является иммутабельным." не является априори верным. Сначала требуется доказать, что множество "мутабельный" действительно является дополнением множества "иммутабельный" до полного множества (всего что возможно быть).
Откуда следует, что множество A является подмножеством множества B? И что A и B не находятся в отношениях пересечения, а не вхождения?Из определения subtyping, которое в ООП языках часто смешивают с наследованием, хотя в целом это разные вещи.
в конструктивной логике тезис "Объект не является мутабельным => объект является иммутабельным." не является априори вернымЭто не тезис, это определение. Там нет никакой импликации, требующей доказательства.
А если б понадобилась, то доказательством импликации A => B в конструктивной логике служит банально функция из A в B, которая в данном случае тривиальна. А вот про дополнения бесконечных множеств в этой логике - это уже совсем пурга, сорри.
Из определения subtyping,Откуда следует, что для A и B выполняется отношение subtyping?
ps
Я о том, что из использования языковой операции наследования или subtyping-а не следует автоматически, что A и B находятся в отношениях subtyping. Из использования операции наследования/subtyping-а следует только, что компилятор думает, что A и B находятся в таких отношениях, но не следует что они действительно в таких отношениях находятся.
Там нет никакой импликации, требующей доказательства.Это не определение и есть импликация, требующая доказательства.
Определения в данном случае:
mutable - это bla-bla1
immutable - это bla-bla2
Утверждения, требующие доказательства:
не immutable => mutable
не mutable => immutable
При определениях вида 1:
- immutable object is an object whose state cannot be modified after it is created
- mutable object, which can be modified after it is created
такое доказательство можно построить.
При определениях вида 2:
- an object is considered immutable even if some internally used attributes change but the object's state appears to be unchanging from an external point of view
- mutable object, which can be modified after it is created
такое доказательство построить нельзя.
http://en.wikipedia.org/wiki/Immutable_object
не mutable => immutableЕсли взять определение mutable object и построить его отрицание, получишь дословно определение immutable object, о чем и речь.
При определениях вида 1:
- immutable object is an object whose state cannot be modified after it is created
- mutable object, which can be modified after it is created
Если взять определение mutable object и построить его отрицание, получишь дословно определение immutable object, о чем и речь.Во-первых, immutable не всегда определяется таким образом.
Во-вторых, из двух определений: "A - когда условие", "im-A - когда не выполняется условие" не всегда следует, что "не A => im-A" и "не im-A => A"
Во-вторых, из двух определений: "A - когда условие", "im-A - когда не выполняется условие" не всегда следует, что "не A => im-A" и "не im-A => A"Ну попробуй это обосновать тогда. Приведи настоящий пример хотя бы.
Очень странное место ты выбрал для вспоминания о небулевой логике, сильно сомневаюсь, что в этом конкретном месте это имеет смысл.
const-object: помечен модификатором const и не имеет внутри пометки mutable
не-const-object: не помечен модификатором const
"не const-object => не-const-object" - не верно
"не не-const-object => const-object" - не верно
Lazy-ФЯ с точки зрения компилятора:
mutable - меняет свое состояние в произвольный момент
im-mutable - не меняет своего состояния от рождения до смерти
lazy - меняет своё состояние от создания до момента обращения, не меняет своё состояние после первого обращения
"не im-mutable => mutable" - не верно
"не mutable => im-mutable" - не верно
ps
1. похоже на конструктивную логику в которой не верно утверждение "не не A => A"
2. для верности утверждения "не im-A => A" необходимо, чтобы im-A было дополнением к A до всего множества
C++:Откуда это определение?
const-object: помечен модификатором const и не имеет внутри пометки mutable
По-моему, все проще и бинарнее: или есть модификатор const, или нет. К иммутабельности отношения не имеет, const объект легко может меняться, т.к. другие места программы могут иметь на него не-const ссылки, а учитывая возможность наличия mutable так и вовсе. Наличие mutable полей не создает третьего рода, это просто такая constness дырявая в С++. Или даже не дырявая, просто обозначает довольно специфичный контракт.
Lazy-ФЯ с точки зрения компилятора:Это еще откуда? Из ленивых языков я знаю Хаскель и Клин. В хаскеле mutable значение легко может быть ленивым, а однажды вычисленное immutable значение может менять свое внутреннее представление несколько раз (например, GC убирает некоторые indirections когда копирует значения между поколениями). Т.е. с точки зрения рантайма там все mutable, по большому счету. А с т.з. языка этого не видно, там любое поле/объект или мутабельно или нет, опять булева дихотомия. Вот в Клине интереснее, там еще доступ к мутабельным объектам может быть разный (похоже на Раст), но вряд ли ты имел его в виду.
im-mutable - не меняет своего состояния от рождения до смерти
mutable - меняет свое состояние в произвольный момент
lazy - меняет своё состояние от создания до момент обращения, не меняет своё состояние после первого обращения
Ну это все частности. Идея твоя с потенциальным числом вариантов (им)мутабельности больше двух понятна, просто непонятно откуда и зачем она высосана на ровном месте.
просто непонятно откуда и зачем она высосана на ровном месте.Появляется при конструировании аспектной системы типов.
Такое бывает:
- или когда используется мощный язык, в котором (им)мутабельность есть возможность встроить через библиотеку
- или когда проектируются внутренности языка, поддерживающего (им)мутабельность
Т.е. с точки зрения рантайма там все mutable, по большому счетуС точки зрения оптимизирующего компилятора/runtime важно множество частных утверждений, а не "по большому счету". Соответственно, появляется множество различного рода частных (им)мутабельностей.
Оставить комментарий
sergeikozyr
ГыгСейчас разбираюсь с одним дерьмом на Питоне, за проведённое с этим говном время успел понять, откуда идёт "лучшее враг хорошего" плюс в сознании непоколебимо стоит охуение от тупости подхода к реализации полиморфизма через наследование, из-за которого как раз приходится пробираться через многофайловые дебри.