yigal_s: (Default)
yigal_s ([personal profile] yigal_s) wrote2004-05-29 08:46 pm

Программистское. Счас спою, или вечерний гон.

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

Самая мерзкая вещь, которую я только знаю в ООП - это так называемое "наследование". То, что наследование нахрен не нужно, можно понять, изучив COM, прекрасно себе работающий без всякого "наследования базовых классов", а лишь посредством имплементации интерфейсов (первейшее средство обеспечения полиморфизма, между прочим). Инкапсуляция и Полиморфизм_посредством_имплементации_интерфейсов_а_не_посредством_чего_нибудь_ещё - вот, что такое ООП. "Наследование" же затесалось в представление об ООП по чистому недоразумению.

Теперь к конкретным примерам.

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

Так вот, всё это очень спорная практика.

Поскольку уже в самых основах можно видеть, что подобное "двойное" наследование в одну сторону вовсе не всегда то, что нужно. Рассмотрим, к примеру, в качестве такой "основы" тип действительных и комплексных чисел. Ясно совершенно, что действительное число - частный случай комплексного. Поэтому, если оформлять их в виде классов, то действительный тип должен наследовать комплексный. Именно так и не иначе. Комплексный тип - базовый. А вот с имплементацией всё ровно наоборот, разумеется, - комплексный тип, скорее уж, использует имплементацию действительного типа (что, впрочем, в данном случае разумнее оформить не через наследование имплементации "в другую сторону", но это частности).

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

Хотя квадрат и является разновидностью прямоугольника (не говоря уже о том, что и ромба тоже), поспешно было бы наследовать квадрат от прямоугольника. И проблема тут даже не в том, что квадрату вовсе не нужно, как прямоугольнику, хранить и длину и ширину (мы уже обсудили абзацем выше, что не всегда направление наследования интерфейса и направление наследования имплементации совпадают). Проблема здесь в том, что прямоугольник в программировании и прямоугольник в математике вовсе не одно и то же. Как уже было сказано в прошлых постах ЖЖ, в математике "нет оператора присваивания", нет переменных. В програмировании же всё с точностью до наоборот. И если у нашего прямоугольника имеется метод
void Scale(real factorX, real factorY); ,
растягивающий его по ширине и высоте, то квадрат этот метод уже никак (адекватным образом) имплементировать не сможет. Неконстантный квадрат - вовсе не частный случай неконстантного прямоугольника.

[* Конец гона *]

А вот завтра пойду на работу и опять чего-нибудь унаследую.

[identity profile] cousin-it.livejournal.com 2004-06-05 08:01 am (UTC)(link)
OK. Можете привести содержательный подтверждающий пример?(вопросы эффективности побоку, интересен именно случай, когда IsA принципиально нельзя превращать в наследование)

Take a look at this. It ain't mine, but it's nice.

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

What do you mean? Is there such a language?

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

Is there such a language? The idea is quite novel and I can't tell outright whether it's practical or not. A prototype would help =))

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

It does create a problem: the programmer can't infer the type of the result by simply looking at an arithmetic expression. He will insert explicit casts out of fear - just like people insert dummy "catch" clauses everywhere in Java, out of fear. This leads to poor coding habits; a programming language should first and foremost be unambiguous to the human.

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

Yes, that's the point. Machine integers aren't machine floats =)

Actually I think we don't disagree all that much =)