Автор Тема: Почему наследование не может быть основой ОС?  (Прочитано 54729 раз)

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #75 : Январь 31, 2013, 05:00:33 am »
Кстати, ссылка на Б. Лискова либо некорректна, либо... автор заблуждается. Использовать надо объект того класса, который нужен... Класс-потомок можно использовать вместо класса-предка, но поведение класса-потомка может кардинально отличаться от поведения класса-предка (поэтому "использовать всюду"... нонсенс).

http://ru.wikipedia.org/wiki/Принцип_подстановки_Барбары_Лисков
Цитировать
Пусть  является свойством, верным относительно объектов  некоторого типа . Тогда  также должно быть верным для объектов  типа , где  является подтипом типа .

Роберт С. Мартин определил[3] этот принцип так:

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

Если "поведение класса-потомка может кардинально отличаться от поведения класса-предка", то это являетя нарушением принципа подстановки Лисков.
А если так подумать, то может быть такому потомку нечего делать в этой иерархии классов?
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

alexus

  • Гость
Re: Почему наследование не может быть основой ОС?
« Ответ #76 : Январь 31, 2013, 05:31:57 am »
Потому что это будет явно и прозрачно, с полной ответственностью программиста за то, что он делает.

Он объявляет свой MegaButton как расширение AbstractButton - он должен определить ВСЕ методы AbstractButton, подумать над ними - может быть, значительная часть из них и будет тупо состоять из вызова this.base.Method() (что тут является аналогом супервызова) - но он будет явно принимать это решение.
А не тупо "перекрыть-переклепать" то, что ему там не нравится, в надежде, что всё остальное будет "дёргаться" автоматически.
Конечно, не надо создавать такие классы, как в VCL - с десятками методов. В большинстве случаев, если у объекта больше десятка методов, его уже пора "резать" на несколько.
То есть, "обжегшить на молоке... дуем на воду"? Зачем надо каждый раз переопределять ВСЕ методы, если требуется добавить один новый или переопределить один существующий? К чему приведёт такая практика? Очевидно, к частому использованию "copy & paste", со всеми вытекающими...
Проблемы VCL, конечно, ощутимы, но связаны они, IMHO, с другими факторами. Первый фактор - желание максимально использовать готовые "элементы управления" (controls), которые есть в Windows. По сути, часть VCL просто "обёртка" над этими "элементами управления"... не слишком удачно связанная иерархией наследования. Второй фактор - отсутствие полноценной агрегации, примером чему являются меню, toolbuttons, grids и пр. Третий фактор - включение в VCL не визуальных (левых) элементов. И т.д. и т.п.
Другими словами, у VCL типичная проблема с архитектурой... желание быстрого результата с минимальными усилиями...

Посмотрел. Там наследование, например, применяется для реализации разных типов атрибутов.
[Речь о проекте СУБД]
Не только, и разных типов отношений (таблиц)... Можно наследовать и более высокоуровневые агрегаты, но об этом не говорилось...

Атрибут (Фиксированный размер (числа, временные), Произвольный размер (строки, blob)).

Конечно, наследование тут полезно, но не обязательно реализации.
Можем сделать для них дерево абстрактных классов без реализаций.
Зачем?.. Предположим, что мы реализовали класс "Текстовая Строка", от неё можно наследовать классы специфических строк, например URL, e-mail и пр. При этом вся логика работы со строками сохраняется... Зачем всё переписывать?

Затем уже делаем реализацию каждого "терминального" класса из дерева. Реализации полностью независимы друг от друга, ничего не знают друг про друга. Потом замечаем, что реализация, допустим, блоба, в чём-то начинает повторять код реализации строк. Мы сразу же выносим этот код в некоторую базовую библиотеку, состоящую из функций или наших служебных классов. Т.е. код, который бы хотелось наследовать (засунуть в класс "Произвольный размер", чтобы потом от притащился автоматом при наследовании и в строки, и в блобы) мы просто выносим отдельно в свои служебные модули. Возникнут какие-то внутрение служебные классы, в которых будет зафиксирована вот эта общность реализаций разных атрибутов.
Не получится... Дело в том, что "засунуть в библиотеку" можно простой алгоритм... а если этот алгоритм требует активной работы с полями класса/объекта? То как быть? Представлять все нужные поля классов в виде формальных параметров? А обращения к другим методам класса, заменять обращением к библиотечным подпрограммам?.. каждый раз передавая огромное количество параметров?.. И Вы считаете, что такая практика способна снизить количество ошибок?..
Илья Евгеньевич, попробуйте подумать в таком направлении... В своё время структурное программирование сыграло огромную позитивную роль в наведении порядка в коде программ. Достигался порядок, прежде всего, за счёт декомпозиции кода по подпрограммам и вынесения общих подпрограмм в библиотеки. Помимо порядка... повышалась надёжность. Но... Подпрограмма без данных... это очень частный случай. А данные были в программе и могли быть "случайно" изменены... Требовалось защитить часть данных, нужных подпрограмме для корректной работы. И вот... (дальнейшие рассуждения я опускаю, Вы с ними знакомы)... логически пришли к классам/объектам, где данные и код инкапсулированы. Теперь надо обеспечить расширяемость классов, так, чтобы повторно не переписывать уже отлаженный код. Появилось наследование... А Вы говорите: "Долой наследование! Давайте каждый раз будем переписывать код!". Зачем?.. Класс - это логичное продолжение декомпозиции, но не только кода, но и данных... наряду с механизмами развития (уточнения под конкретные условия применения). Но это и иная плоскость мышления... Написание библиотек (общего пользования) кардинально отличается от написания программ. Написание иерархии классов... тем более. Классы (иерархии классов) в программах избыточны, они нужны в системах. Для написания программ, как правило, достаточно подпрограмм (библиотек)... Это мы тоже обсуждали.

alexus

  • Гость
Re: Почему наследование не может быть основой ОС?
« Ответ #77 : Январь 31, 2013, 05:33:54 am »
К сожалению, Н. Вирт видимо так и не понял возможностей ОО-концепции, что сказалось и на Обероне, и... на его адептах.
Как по-Вашему, в каких языках наиболее правильно реализованы концепции ООП?
SmallTalk

alexus

  • Гость
Re: Почему наследование не может быть основой ОС?
« Ответ #78 : Январь 31, 2013, 05:52:33 am »
Кстати, ссылка на Б. Лискова либо некорректна, либо... автор заблуждается. Использовать надо объект того класса, который нужен... Класс-потомок можно использовать вместо класса-предка, но поведение класса-потомка может кардинально отличаться от поведения класса-предка (поэтому "использовать всюду"... нонсенс).

http://ru.wikipedia.org/wiki/Принцип_подстановки_Барбары_Лисков
Цитировать
Пусть  является свойством, верным относительно объектов  некоторого типа . Тогда  также должно быть верным для объектов  типа , где  является подтипом типа .

Роберт С. Мартин определил[3] этот принцип так:

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

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

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #79 : Январь 31, 2013, 06:13:38 am »
К сожалению, Н. Вирт видимо так и не понял возможностей ОО-концепции, что сказалось и на Обероне, и... на его адептах.
Как по-Вашему, в каких языках наиболее правильно реализованы концепции ООП?
SmallTalk
Видимо, SmallTalk-80 и далее? (В SmallTalk-72, например, не было наследования...)
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #80 : Январь 31, 2013, 06:23:49 am »
Класс-предок задаёт "обобщённое поведение", классы-наследники уточняют поведение. При этом уточнение может кардинально отличаться от того, что замышлялось при создании класса-предка. Предположим, что мы говорим о транспорте. Когда создавалась первая телега... вряд ли кто-то серьёзно думал о пассажирских самолётах. То есть, создавая абстракцию, "транспортное средство" трудно предположить, во что оно может вылиться... Правильнее думать о сути: "перемещение людей и грузов на расстояние", а не о "поведении", как таковом.
Другими словами, предположения о схожести "поведения" предка и потомка являются надуманными (если уж говорить, то у функциональном подобии предка и потомка, а не о их поведении).

Тогда, может быть, следует делать так:

Использовать наследование структуры и приватных методов для уточнения поведения объектов, но при этом использовать реализацию интерфейсов для полиморфного использования объектов?
Тогда, по идее, и принцип подстановки не будет нарушен, если функция, использующая этот объект, сможет дёргать только методы интерфейса.
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #81 : Январь 31, 2013, 06:53:21 am »
Если принцип подстановки не использовать НИКОГДА, то можно обйтись и без наследования.
Но вопрос требует более тщательного изучения: так ли уж нужен этот самый принцип подстановки ? В конце-концов, обходились же раньше без него... :)

В смысле, Вы хотите отказаться от полиморфизма? Это полностью убивает суть ООП, как основы для расширяемых систем.

Речь-то идёт не про "без наследования", а про "не делайте неабстрактные классы доступными для наследования, не позволяйте от них наследоваться никому. А в абстрактных классах не делайте реализацию никаких функций, или только совершенного минимума функций. Не используйте нигде в программе имена неабстрактных классов, в том числе в new, используйте фабрики, фабрики, ещё раз фабрики".

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #82 : Январь 31, 2013, 07:08:56 am »
Не получится... Дело в том, что "засунуть в библиотеку" можно простой алгоритм... а если этот алгоритм требует активной работы с полями класса/объекта? То как быть? Представлять все нужные поля классов в виде формальных параметров? А обращения к другим методам класса, заменять обращением к библиотечным подпрограммам?.. каждый раз передавая огромное количество параметров?.. И Вы считаете, что такая практика способна снизить количество ошибок?..
А это будет объектно-ориентированная библиотека.
Например, если у строки и блоба есть что-то общее в хранении данных, то возникнет некий класс технического назначения BinaryStorage, допустим, и мы будем его использовать и внутри реализации строки, и внутри реализации блоба.

Цитировать
И вот... (дальнейшие рассуждения я опускаю, Вы с ними знакомы)... логически пришли к классам/объектам, где данные и код инкапсулированы. Теперь надо обеспечить расширяемость классов, так, чтобы повторно не переписывать уже отлаженный код. Появилось наследование... А Вы говорите: "Долой наследование! Давайте каждый раз будем переписывать код!".
Здесь Вы пропустили логический этап: сначала расширяемость классов нужна, чтобы выражать отношения "род-вид" (и обеспечивать полиморфизм, расширяемость системы, когда я могу всюду, где нужна "рыба", дать и хоть "селёдку", хоть "камбалу"). То, что в Симуле при этом сразу же решили через это проблему и повторного использования кода из базовых классов, не означает автоматически, что это лучшее решение. Позже появился подход (не уверен, кто его начал первым пропагандировать - возможно всё же Гамма и Ко. в книге "Паттерны ООП"), рекомендующий устанавливать отношения наследования между абстрактными классами, а классы, реализующие эти абстракции, делать "финальными" и недоступными "по имени".

Вопрос "зачем"? Я придерживаюсь этого подхода потому, что он вынуждает разбивать систему на более мелкие части и значительно понижает зависимость между частями. Финальные классы, которые содержат реализацию, ничего не знают друг о друге (я могу жонглировать разными реализациями хоть прямо во время выполнения, просто "воткнув" другую фабрику, а попробуйте Вы подмените на ходу реализацию String или Blob. Даже попробуйте просто поддерживать 2-3 реализации String. У Вас возникнет 2-3 версии ОДНОГО КЛАССА, а значит - ОДНОГО ФАЙЛА. Бррр. А у меня эти 2-3 реализации будут в разных финальных классах и жить будут параллельной жизнью).
Если применять наследование реализации в случае с атрибутами, то, например, та самая техническая абстракция BinaryStorage, являющаяся "общим знаменателем" и для String, и для Blob, просто не возникнет. Её код будет "вмазан" в абстракцию "Атрибут переменной длины" (вмазан в абстрактное понятие!!!).
А так этот BinaryStorage становится полезным техническим средством и, заметьте, совершенно независимым от других абстракций Вашей СУБД. Вы его можете отделить от своей СУБД и "подарить" кому-нибудь, кто делает другую СУБД :)
И, кстати, ещё спорный вопрос, понадобятся ли классы "Атрибут переменной длины" и "Атрибут постоянной". Они понадобятся, только если будут выражать какую-то общность в интерфейсах этих атрибутов. А сейчас, возможно, они присутствуют только потому, что выражают общность реализации.
Вот тоже важная концептуальная проблема - в случае наследования реализации по какому признаку-то строим обобщающую иерархию - по признаку общности интерфейса или по признаку общности реализации - "можно удобно впендючить в этот пром. класс полезную функцию для всех его потомков"? Мешаются в кучу кони и люди.

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #83 : Январь 31, 2013, 07:42:06 am »
Вот пример того, как могут возникать противоречия при использовании наследования реализации.

Ниже пример - условен, поэтому просьба не "въедаться" в духе "а зачем в СУБД такое" и проч.

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

Лучше спроектировать дерево абстракций без примешивания вопросов повторного использования кода. А для повторного использования кода потом ввести внутреннюю систему классов.

Valery

  • Full Member
  • ***
  • Сообщений: 101
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #84 : Январь 31, 2013, 09:45:26 am »
Если принцип подстановки не использовать НИКОГДА, то можно обйтись и без наследования.
Но вопрос требует более тщательного изучения: так ли уж нужен этот самый принцип подстановки ? В конце-концов, обходились же раньше без него... :)

В смысле, Вы хотите отказаться от полиморфизма? Это полностью убивает суть ООП, как основы для расширяемых систем.

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

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Почему наследование не может быть основой ОС?
« Ответ #85 : Январь 31, 2013, 11:53:33 am »
Особенно доставляет "веселье" наследование от неабстрактного класса для объектов которого написан свой менеджер памяти (в "деструкторе" указатели на объекты пихаются в кэш для последующего повторного использования).


valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #86 : Январь 31, 2013, 12:48:09 pm »
Особенно доставляет "веселье" наследование от неабстрактного класса для объектов которого написан свой менеджер памяти (в "деструкторе" указатели на объекты пихаются в кэш для последующего повторного использования).
Это шарповое что-то?

В той же Аде много культурней все это делается.
Y = λf.(λx.f (x x)) (λx.f (x x))

alexus

  • Гость
Re: Почему наследование не может быть основой ОС?
« Ответ #87 : Январь 31, 2013, 12:54:44 pm »
Класс-предок задаёт "обобщённое поведение", классы-наследники уточняют поведение. При этом уточнение может кардинально отличаться от того, что замышлялось при создании класса-предка. Предположим, что мы говорим о транспорте. Когда создавалась первая телега... вряд ли кто-то серьёзно думал о пассажирских самолётах. То есть, создавая абстракцию, "транспортное средство" трудно предположить, во что оно может вылиться... Правильнее думать о сути: "перемещение людей и грузов на расстояние", а не о "поведении", как таковом.
Другими словами, предположения о схожести "поведения" предка и потомка являются надуманными (если уж говорить, то у функциональном подобии предка и потомка, а не о их поведении).

Тогда, может быть, следует делать так:

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

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Почему наследование не может быть основой ОС?
« Ответ #88 : Январь 31, 2013, 12:55:54 pm »
Особенно доставляет "веселье" наследование от неабстрактного класса для объектов которого написан свой менеджер памяти (в "деструкторе" указатели на объекты пихаются в кэш для последующего повторного использования).
Это шарповое что-то?

В той же Аде много культурней все это делается.
Это моё. Когда надо создать новый объект, то сначала смотрю в кэше. Если кэш не пустой, то выдаю указатель на готовый объект. Если кэш пустой, то создаю объект по-настоящему. Когда объект "удаляется", то помещаю указатель в кэш для повторного использования. Пишу на C#, но C# тут не при чём.

А в Аде чего?


valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #89 : Январь 31, 2013, 01:02:23 pm »
Особенно доставляет "веселье" наследование от неабстрактного класса для объектов которого написан свой менеджер памяти (в "деструкторе" указатели на объекты пихаются в кэш для последующего повторного использования).
Это шарповое что-то?

В той же Аде много культурней все это делается.
Это моё. Когда надо создать новый объект, то сначала смотрю в кэше. Если кэш не пустой, то выдаю указатель на готовый объект. Если кэш пустой, то создаю объект по-настоящему. Когда объект "удаляется", то помещаю указатель в кэш для повторного использования. Пишу на C#, но C# тут не при чём.

А в Аде чего?
В Аде, сколь я помню, есть полноценные Storage_Pool'ы. В том числе там есть ограничения, что ссылки у тех кто сидит внутри такого пула могут быть только на тех кто в том же пуле. Это проверка статическая, компилятором.

По сути, это такие отдельные кучи/кучки внутри программы.
Y = λf.(λx.f (x x)) (λx.f (x x))