LINUX.ORG.RU

ООП без множественного наследования.

 ,


1

2

А не кажется ли вам, господа, что, сабж попахивает противоречием. Например, Вася работает слесарем, а в свободное время играет в футбол. В множественном мы просто наследуем его от футболиста и слесаря, а как без него это выразить? Миксины я не беру в расчет, они в Ъ не катят. А как еще? Никак.

Допустим, я насколько знаю, в Смоллтоке нет сабжа. Я честно говорю, уважаю Алана Кея, он говорит много правильных вещей, его позиция и взгляды мне близки. Но тут ИМХО, он сделал ошибку. Как вы считаете?

Ты уже кажись вбрасывал на эту тему, не?

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

ты уже нарисовал окружность на канве?

В JS же нет множественного наследования. Как ты предлагаешь анонiмусу это сделать без него?!

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

окружность рисуется элементарно, на canvas

arc(100, 100, 50, 0, Math.PI*2, false); 
там речь шла не об окружности, а о создании ооп интерфейса для рисования окружностей. Уже забыли, блин, а умничают сидят:)

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

Сложного я тут ничего не вижу.

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

Ну это как-то неубедительно. Что, если он матерится, как футболист, но бухает, как слесарь? Ко всему прочему я должен уметь скастовать Васю к нужному предку и вызвать метод. А что если эти предки сами наследуются от класса быдло (уж простите, не со зла), инстансы должны шариться или в Васе два быдла живёт? Короче, излишне сложно это всё.

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

Что, если он матерится, как футболист, но бухает, как слесарь?

значит надо переопределить один метод. Откатить. ничего сложного тут нет.

А что если эти предки сами наследуются от класса быдло (уж простите, не со зла), инстансы должны шариться или в Васе два быдла живёт?

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

Короче, излишне сложно это всё.

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

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

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

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

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

Нет. Миксины противоречат концепции метаобъектов.

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

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

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

Что, если он матерится, как футболист, но бухает, как слесарь?

class D : public B, public C
{
    using B::foo;
    using B::bar;
};

Ко всему прочему я должен уметь скастовать Васю к нужному предку и вызвать метод. А что если эти предки сами наследуются от класса быдло (уж простите, не со зла), инстансы должны шариться или в Васе два быдла живёт?

class A { public: void foo(){} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

int main()
{
    D d;
    d.foo();
}

Если надо, чтобы «быдло размножалось» - убираешь virtual.

Короче, излишне сложно это всё.

Наоборот, крайне просто, в том числе что касается кода. Как ты видишь ситуацию - так ее и выписываешь. И это выглядит не как длинная портянка, в которой ты пытается понять who is who, а как прямая документация.

anonymous
()

А как еще?

Способов много. Например интерфейсы. Во многом сервис-локаторов достаточно.

Вообще ООП это сплошные модели и упрощения с компромиссами. Зачем тащить и притягивать за уши избыточность?

они в Ъ не катят

как раз наоборот!

Как вы считаете?

Поколение пэпси уже прочитал?

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

А сложно в плане реализации в языке имелось в виду.

На самом деле нет. У меня есть свое недо-ООП для С++ - но с рефлексией, динамикой и пр. плюшками, так это все разруливается совсем небольшим и простым куском кода.

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

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

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

Вообще ООП это сплошные модели и упрощения с компромиссами. Зачем тащить и притягивать за уши избыточность?

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

как раз наоборот!

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

javaQest
() автор топика

Допустим, я насколько знаю, в Смоллтоке нет сабжа.

В Smalltalk-80 нет, в Pharo из коробки есть Traits

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

Что, если он матерится, как футболист, но бухает, как слесарь?

Ящитаю, что подобные вещи не должны отражаться в иерархии классов

yoghurt ★★★★★
()

Интерфейсы же.

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

И Вася будет содержать футболиста и слесаря?

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

«Проблема ромба»(которая вовсе не проблема) - это вопрос проектирования, а не реализации на том или ином языке. Иногда нужно чтобы база была общая, иногда - чтобы она была разной от разных предках. C++ позволяет выразить это на уровне классов, Eiffel умеет это даже на уровне полей.

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

Да. Насколько я знаю, там вообще по большей части на примесях и делается.

javaQest
() автор топика

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

footballNow(person p)
{
    ...
}
sayFuckYou(person p)
{
    ...
}
person Vasja = {age:30,...};
footballNow(Vasja);
sayFuckYou(Vasja);
они думают о множественном наследовании, миксинах, трейтах, интерфейсах...

deep-purple ★★★★★
()
Ответ на: комментарий от mix_mix

Да ну, ты только посмотри на layout таблицы виртуальных функций в тех же плюсах. В динамике проще, конечно, да.

Тут скорее не в динамике дело, а в выбранном методе реализации. В популярных реализациях С++ ее, очевидно, сделали с упором на максимальную эффективность. Ну и vtable не так уж страшен как кажется, если почитать хорошую доку с примерами, то все становится просто и логично. И кстати без динамики тут все-равно никак, ведь тот же dynamic_cast будет работать уже в рантайме. В том числе потому, что класть еще один «левый» указатель в каждый объект было бы слишком жирно по меркам С++.

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

Как ты видишь ситуацию - так ее и выписываешь.

Вот только на момент написания B и C мы можем не предполагать о том что кому-то захочется наследоваться от обоих классов.

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

Вот только на момент написания B и C мы можем не предполагать о том что кому-то захочется наследоваться от обоих классов.

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

anonymous
()

то есть, если мне нужен слесарь, то придется звать васю? :(

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

Что за глупости? Ты пр проектировании класса всегда знаешь, предназначен ли он для наследования. И как правило знаешь, виртуальным оно должно быть или нет. Это если думать, что делаешь.

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

Иногда нужно чтобы база была общая, иногда - чтобы она была разной от разных предках.

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

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

и реализация ромба в с++ нарушает такое естественное положение

Для тугих выше уже писалось - в С++ ты сам лично выбираешь как будет реализован ромб.

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

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

Что такое «оставались независимы»? И при чем тут объекты, если наследуются классы?

Но вообще по умолчанию в C++ как раз будет «независимое» наследование - будет две копии базового класса.

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

порой такая неоднозначность может выйти боком. и как ее разрешить?

Какая неоднозначность? Можно примеры?

anonymous
()

На самом деле ты не хочешь наследоваться от футболиста и слесаря. Ты хочешь совместить умения футболиста и слесаря в одном человеке. И тут сразу становится понятно, что есть классы «умение слесаря», «умение футболиста», «футболист, имеющий умения футболиста (has)», «слесарь, имеющий умения слесаря (has)», и наконец наш «слесарь-футболист с (has) умениями и слесаря, и футболиста». Паттерн называется стратегия.

Представьте, что у вас есть пилот межгалактического корабля и футболист. Вы хотите объединить такого пилота и футболиста. Наследуетесь от обоих, но так получилось, что раньше пилотами межгалактических кораблей являлись только инопланетяне. В итоге получилось наследование от человека и инопланетянина одновременно, которое не разруливается ромбом (виртуальным наследованием). Вы создаете 2 класса: пилот-человек и пилот-инопланетянин. Вроде, логично, но точно пошли не в ту сторону.

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

Ты пр проектировании класса всегда знаешь, предназначен ли он для наследования.

«Предназначен для наследования» и «планируется наследование от нескольких потомков одновременно» - разные вещи.

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

Ну-ну.

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

На самом деле ты не хочешь наследоваться от футболиста и слесаря. Ты хочешь совместить умения футболиста и слесаря в одном человеке. И тут сразу становится понятно, что есть классы «умение слесаря», «умение футболиста», «футболист, имеющий умения футболиста (has)», «слесарь, имеющий умения слесаря (has)», и наконец наш «слесарь-футболист с (has) умениями и слесаря, и футболиста». Паттерн называется стратегия.

Очевидно это был просто пример без привязки к реальной нужности наследования. А для твоей интерпретации никакой «паттерн стратегия» не нужен, он тут вообще к месту и решает совсем другую задачу. Здесь подойдут обычные и банальные интерфейсы.

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

Код может быть библиотечным.

Во-первых наружу обычно выдают самый минимум без деталей реализации. Разве что для оптимизации узких мест. Во-вторых, если вдруг оно таки на виду, и его таки приходится менять - то раз надо, то надо. Выпускаем следующую версию библиотеки с этим изменением, старую поддерживаем на уровне багфиксов. В чем проблема? Так поступают все абсолютно. Даже, если взять простой С без всякого ООП, то ты не сможешь собраться, например,с одной версией libjpeg, а потом использовать более новую. Надо пересборка. И авторы специально добавили эту проверку. Аналогично - слинковавшись с одной ICU ты не сможешь взять другую. Ну и т.д. Очевидные же веши.

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

наружу обычно выдают самый минимум без деталей реализации

Это про API, а есть ещё ABI, который сохранить немного сложнее.

то ты не сможешь собраться, например,с одной версией libjpeg, а потом использовать более новую

Можешь, если не сменилось ABI библиотеки. И я даже больше скажу - в хороших библиотеках стараются ABI не ломать между релизами (хотя бы минорными).

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

Это про API, а есть ещё ABI, который сохранить немного сложнее.

WAT? Если изменения только в реализации, а условно говоря публичные хедера не меняются, то и ABI сохраняется.

Можешь, если не сменилось ABI библиотеки.

Теоретники в треде, специально для них - там есть проверка в рантайме.

И я даже больше скажу - в хороших библиотеках стараются ABI не ломать между релизами (хотя бы минорными).

А кто тебе сказал, что его не надо пытаться сохранить? Надо, но это не отменяет того факта, что его еще и надо ломать. И не стоит в этом вопросе пытаться быть святее самого Папы.

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

«Предназначен для наследования» и «планируется наследование от нескольких потомков одновременно» - разные вещи.

Нет.

Ну-ну

Приведи примеры, рассмотрим.

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

В чем проблема?

В том что писать библиотеку и использующий её код могут (и скорее всего будут) разные люди.

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

В том что писать библиотеку и использующий её код могут (и скорее всего будут) разные люди.

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

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