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

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Почему наследование не может быть основой ОС?
« Ответ #15 : Январь 26, 2013, 04:27:49 pm »
Valery Solovey, вот конкретный пример из ББ:
Stores.Store* = POINTER TO ABSTRACT RECORD
dlink: Domain;
era, id: INTEGER; (* externalization era and id *)
isElem: BOOLEAN (* to preserve file format: is this an elem in the old sense? *)
END;
...
Views.View* = POINTER TO ABSTRACT RECORD (Stores.Store)
context-: Models.Context; (** stable context # NIL **)
era: INTEGER;
guard: INTEGER; (* = TrapCount()+1 if view is addressee of ongoing broadcast *)
bad: SET
END;
...
Containers.View* = POINTER TO ABSTRACT RECORD (Views.View)
model: Model;
controller: Controller;
alienCtrl: Stores.Store (* alienCtrl = NIL  OR  controller = NIL *)
END;
...
TextViews.View* = POINTER TO ABSTRACT RECORD (Containers.View) END;
...
TextViews.StdView = POINTER TO RECORD (View)
(* model *)
text: TextModels.Model;
org: INTEGER;
dy: INTEGER; (* 0 <= dy < Height(first line) *)
defRuler: TextRulers.Ruler;
defAttr: TextModels.Attributes;
hideMarks: BOOLEAN;
(* general state *)
cachedRd: TextSetters.Reader;
(* line grid cache *)
trailer: Line; (* trailer # NIL => trailer.eot, trailer.next # trailer *)
bot: INTEGER; (* max(f : f seen by Restore : f.b) *)
(* setter *)
setter, setter0: TextSetters.Setter (* setter # setter0 lazily detects setter change *)
END;
Есть модель хранилища -->
Расширили до представления -->
Расширили до контейнера -->
Расширили до текстового представления.

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Почему наследование не может быть основой ОС?
« Ответ #16 : Январь 26, 2013, 04:31:11 pm »
В моей практике (С++) классическое наследование реализации работает крайне плохо.
А никто вроде и не говорил о наследовании реализации.

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #17 : Январь 26, 2013, 05:08:02 pm »
Так я же не говорю, что принципиально нельзя. Я говорю, что есть вещи, которые действительно управляют расширением модели, и они иногда приводят к наследованию. А если вспоминать только про наследование, то очень легко забыть (или не понять, если читает новичок) саму идею.

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Почему наследование не может быть основой ОС?
« Ответ #18 : Январь 26, 2013, 05:27:33 pm »
Вы меня не поняли.
Я просто заменяю "наследование" словом "расширение", т.к. это имхо более правильно звучит.
Да и в Оберонах вроде принято говорить "расширение типа" вместо "потомок класса"

alexus

  • Гость
Re: Почему наследование не может быть основой ОС?
« Ответ #19 : Январь 26, 2013, 06:03:25 pm »
Немного в сторону систем...
Первая проблема операционных систем в том, что они развивались... хаотично, стремясь как-то устранить несовершенство ЭВМ, облегчить взаимодействие с ними. Собственно, до сих пор мне не встречались работы, которые бы рассматривали данное программное обеспечение с системных позиций, как единое целое. Нужно было работать с внешними дисковыми устройствами, появились дисковые операционные системы; потребовалась работа с графикой, добавились графические модули; стало необходимым взаимодействие компьютеров, добавились сетевые модули, интернет, безопасность и пр. и пр. Такой подход к "расширению" имеет вполне очевидный минус... каждый раз надо переписывать огромное количество кода. И тем не менее, такой подход к созданию операционных "систем" сохраняется и видимо в ближайшей перспективе не изменится. Чтобы данный подход изменился требуется ответить на такие вопросы, какова цель вычислительной техники, на каком этапе её развития мы находимся, что нас ожидает в перспективе... В принципе, сегодня эти вопросы уже вполне обсуждаемы, поскольку основные тренды развития можно отслеживать и анализировать. Понимая цель, можно говорить о системе, можно рассматривать её составные части и связи между ними... т.е. можно говорить и той части, которая называется "операционная система", её структуре/архитектуре, функциональном наполнении и пр.

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

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #20 : Январь 26, 2013, 06:13:29 pm »
Вы меня не поняли.
Я просто заменяю "наследование" словом "расширение", т.к. это имхо более правильно звучит.
Да и в Оберонах вроде принято говорить "расширение типа" вместо "потомок класса"
Ну, если речь шла про расширение записей...

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

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Почему наследование не может быть основой ОС?
« Ответ #21 : Январь 26, 2013, 06:17:15 pm »
Гадать смысла нет. Нужно ждать комментариев от автора.

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #22 : Январь 26, 2013, 08:29:58 pm »
Valery Solovey, вот конкретный пример из ББ:
...

Но это таки случай Framework - когда вам предоставили базовый набор "заготовок", от которых наследуетесь. Вообще, оно и тут нежелательно, просто в то время, когда закладывался ББ, осознание, что наследование реалиазации надо заменять декомпозицией, ещё толком не пришло....
Что оно сейчас приходит отнюдь не только в пространстве около Оберонов, я отметил здесь: http://oberspace.dyndns.org/index.php/topic,442.msg14062.html#msg14062

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Почему наследование не может быть основой ОС?
« Ответ #23 : Январь 26, 2013, 09:39:51 pm »
наследование реалиазации
Автор говорил именно о наследовании реализации?

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #24 : Январь 26, 2013, 09:56:52 pm »
Там упоминалась возможность наследования нескольких классов с несовпадающими реализациями -- видимо, что бы не создавать конфликтоа при наследовании...
to iterate is human, to recurse, divine

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

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #25 : Январь 27, 2013, 03:05:39 am »
А никто вроде и не говорил о наследовании реализации.

Я как раз так понял, что речь именно о наследовании реализации. Когда говорят о наследовании интерфейсов, то обычно говорят в контексте абстрагирования от реализации.
Впрочем, как я уже говорил, заметки автора весьма черновые. В частности, пример наследования программ в виде "блокнот -> WordPad -> Word", если воспринимать его дословно, не выдерживает никакой критики. Нечего там наследовать. Реализация будет совершенно разная. Можно, конечно, притянуть сюда за уши наследование интерфейсов (типа Word можно выставить через интерфейс блокнота). Но не понятно зачем - поэтому за уши.

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #26 : Январь 27, 2013, 05:45:16 am »
Для тех, кто до конца не понимает, почему наследование реализации - зло и почему его сравнивают даже с goto.

Вот вам строка: obj.DoSomething. Если я знаю, что в системе нет наследования реализации, я понимаю, что именно будет выполненно. Процедура DoSomething того фактического типа, который имеет obj.
Ответьте на тот же вопрос, если система пронизана наследованием реализации. Попробуйте разобраться, как прыгает поток управления, даже при вызове метода одного объекта. Вызвали из своего метода свой же другой метод - управление упрыгало фиг знает в какого предка...
Это ещё один, кстати, пример, откуда потом берётся жирность IDE. Потому что распутывать клубок структуры программы в подобных случаях без поддержки IDE очень трудно.

Второй пример: у вас есть наследование реализации от какого-то типа X. И вдруг вам нужно завести несколько реализаций X. Более простой случай - если реализации не сосуществуют во время выполнения. Например, под разные ОС. Вам придётся иметь две версии одного исходника, как-то их переключать, вместо того, чтобы просто иметь два отдельных класса - реализации базового абстрактного. При наследовании только от абстрактных классов получается придерживаться принципа - каждый вариант реализации существует в виде своего модуля, класса, и т.п. А не дремучая вариативность на уровне исходников.
Ещё хуже, если вам нужно иметь во время выполнения разные реализации X. Т.е. некоторые экземпляры Y должны иметь в своей базе одну реализацию X, некоторые - другую.
Конечно же, знатоки скажут, что ответом на обе проблемы является идиома PImpl (Pointer to implementation), когда вся реализация X выносится в отдельный объект, который указывается при инициализации X. Ну так это и будет, други мои, хороший такой шаг к композиции вместо наследования. И в большинстве случаев будет непонятно, зачем сохранять X, если PImpl может содержать сразу класс, наследуемый от Y.

alexus

  • Гость
Re: Почему наследование не может быть основой ОС?
« Ответ #27 : Январь 27, 2013, 06:15:11 am »
Для тех, кто до конца не понимает, почему наследование реализации - зло и почему его сравнивают даже с goto.
Я ни до конца... ни после начала не понимаю... :)

Вот вам строка: obj.DoSomething. Если я знаю, что в системе нет наследования реализации, я понимаю, что именно будет выполненно. Процедура DoSomething того фактического типа, который имеет obj.
Совершенно верно! Если в советское время мы шли покупать обувь, то однозначно покупали сапоги кирзовые 43 размера... Всем всё было понятно... но не всегда удобно (плата за однозначность понимания.. видимо?).

Ответьте на тот же вопрос, если система пронизана наследованием реализации. Попробуйте разобраться, как прыгает поток управления, даже при вызове метода одного объекта. Вызвали из своего метода свой же другой метод - управление упрыгало фиг знает в какого предка...
Если Вы не знаете, что хотите носить, то вызывайте именно "сапоги кирзовые 43 размера"... кто же против... А мне вот кроссовки нужны на лето...

Это ещё один, кстати, пример, откуда потом берётся жирность IDE. Потому что распутывать клубок структуры программы в подобных случаях без поддержки IDE очень трудно.
Да, иметь выбор всегда... несколько затратнее, чем не иметь оного... Но мы об удобстве или о "шашечках"?

Второй пример: у вас есть наследование реализации от какого-то типа X. И вдруг вам нужно завести несколько реализаций X. Более простой случай - если реализации не сосуществуют во время выполнения. Например, под разные ОС. Вам придётся иметь две версии одного исходника, как-то их переключать, вместо того, чтобы просто иметь два отдельных класса - реализации базового абстрактного. При наследовании только от абстрактных классов получается придерживаться принципа - каждый вариант реализации существует в виде своего модуля, класса, и т.п. А не дремучая вариативность на уровне исходников.
Какая разница... сосуществуют ли объекты во время исполнения или нет?.. Две версии, под каждую "ось" всё равно придётся иметь... И классы будут разными...

Ещё хуже, если вам нужно иметь во время выполнения разные реализации X. Т.е. некоторые экземпляры Y должны иметь в своей базе одну реализацию X, некоторые - другую.
Весьма странный вывод... Совершенно непонятно, на чём он основан... Какая разница для Y с каким экземпляром какого подкласса X он взаимодействует?..

Конечно же, знатоки скажут, что ответом на обе проблемы является идиома PImpl (Pointer to implementation), когда вся реализация X выносится в отдельный объект, который указывается при инициализации X. Ну так это и будет, други мои, хороший такой шаг к композиции вместо наследования. И в большинстве случаев будет непонятно, зачем сохранять X, если PImpl может содержать сразу класс, наследуемый от Y.
А ещё лучше научиться различать красное и горячее... Наследование реализации совсем не означает и не предполагает отсутствия интерфейсов. Просто интерфейсы - это спецификации, не более того.

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #28 : Январь 27, 2013, 06:33:17 am »
Совершенно верно! Если в советское время мы шли покупать обувь, то однозначно покупали сапоги кирзовые 43 размера... Всем всё было понятно... но не всегда удобно (плата за однозначность понимания.. видимо?).

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

Цитировать
Это ещё один, кстати, пример, откуда потом берётся жирность IDE. Потому что распутывать клубок структуры программы в подобных случаях без поддержки IDE очень трудно.
Да, иметь выбор всегда... несколько затратнее, чем не иметь оного... Но мы об удобстве или о "шашечках"?
Иметь столь запутанную структуру, которую нельзя понять простым изучением исходника и просто уложить в голову - Вы называете удобством?

Цитировать
Какая разница... сосуществуют ли объекты во время исполнения или нет?.. Две версии, под каждую "ось" всё равно придётся иметь... И классы будут разными...

Ещё хуже, если вам нужно иметь во время выполнения разные реализации X. Т.е. некоторые экземпляры Y должны иметь в своей базе одну реализацию X, некоторые - другую.

Пусть у Вас есть BaseButton, класс с реализацией. От него выстроено целое дерево наследования всяких разных кнопок. В стиле VCL. И внезапно нам нужна кроссплатформенность. У нас появляется две реализации BaseButton - под Винду и Линукс. Если мы так и оставим код реализации внутри класса BaseButton, то получим две параллельных версии одного класса, одного исходника. Проблему с управлением ими и проч. Большинству, конечно, не привыкать, но я так не хочу - потому что умею делать такие архитектуры, при которых каждый класс существует только в единственном варианте (только линейный порядок дорабатываемых версий). Любая вариативность делается отдельными классами.
Другой выход, как я и говорил, не оставлять код реализации внутри класса BaseButton, а сделать pointer to implementation. Интерфейс ButtonImpl, разные его реализации - и втыкать в BaseButton статическим полем указатель на реализацию.

Но я бы изначально не ввязался с этим Буттоном в трясину наследования.
Имел бы интерфейс AbstractButton и никому не видимую базовую реализацию BaseButton, создаваемую через фабрику.
И в любых расширенных кнопках просто имел бы поле base: AbstractButton, через которое агрегировал BaseButton.

Эффект ясности от такого стиля очень хороший - и мне назад в паутину наследования реализации не хочеццца.

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Почему наследование не может быть основой ОС?
« Ответ #29 : Январь 27, 2013, 06:38:28 am »
Где-то год назад в разговоре с Вами я приводил пример, как архитектура, в которой классы реализаций сокрыты (известны только интерфейсы и фабрики) позволяет решать "системные вопросы". Вам тогда ещё, насколько я помню, понравилось.

Повторю здесь, думаю, уместно:

Цитата: alexus
Строго говоря... элементы не должны взаимодействовать между собой... напрямую. Они должны делать только то, что им предписано... свыше. Сделали и отдали наверх, а кто там и как дальше работает, они не ведают. Если элемент (компонент/объект) А взаимодействует с элементом Б, то это взаимодействие должно быть определено, как минимум в элементе А, но, возможно, и в элементе Б, а это, в свою очередь, означает, что элемент А не может использоваться без элемента Б и, наоборот. Такая связь "цементирует" элементы и лишает систему гибкости (возможности перестраивать связи). "Слабые связи" в системе исчезают... а за ними и сама система становится монолитом, то есть, не-системой.

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

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

MODULE Телефония;

   TYPE
      Линия* = POINTER TO ABSTRACT RECORD END;
      Сеанс* = POINTER TO ABSTRACT RECORD END;
      Реализация* = POINTER TO ABSTRACT RECORD END;

   VAR
      стандартная-: Реализация;

   PROCEDURE (л: Линия) Звонить* (номер): Сеанс, NEW, ABSTRACT;
   PROCEDURE (л: Линия) Входящий* (): Сеанс, NEW, ABSTRACT;

   PROCEDURE (сн: Сеанс) Сказать* (сообщение), NEW, ABSTRACT;
   PROCEDURE (сн: Сеанс) Слушать* (сообщение), NEW, ABSTRACT;
   PROCEDURE (сн: Сеанс) Закрыть*, NEW, ABSTRACT;

   PROCEDURE (р: Реализация) НоваяЛиния* (): Линия, NEW, ABSTRACT;

   PROCEDURE ЗадатьСтандартную* (реализация: Реализация);
   BEGIN
      стандартная := реализация
   END ЗадатьСтандартную;

END Телефония.

MODULE Секретари;
   IMPORT Телефония;

   TYPE
      Секретарь* = ABSTRACT RECORD END;
      Реализация* .... аналогично ....
   
    VAR
      стандартная*: Реализация;

   .... разные процедуры секретаря ...
   PROCEDURE (с: Секретарь) СестьНаТелефон* (линия: Телефония.Линия), NEW, ABSTRACT;
   PROCEDURE (с: Секретарь) Помогать* (кому: Секретарь), NEW, ABSTRACT;

END Секретари.

Реальных реализаций телефонии и секретарей у нас может быть много. В разных модулях. Никто в системе, кроме конфигурирующих процедур, не знает про эти модули.
Стандартные реализации привязывает какой-нибудь конфигурирующий модуль:
MODULE Config;
   PROCEDURE Setup;
   BEGIN
      Телефония.ЗадатьСтандартную(ТелефонияВариант1.реализация);
      Секретари.ЗадатьСтандартную(СекретариВариант1.реализация)
   END Setup;
END Config;

Специфические для нашего приложения особенности вынесем в наш модуль конфигурации:
MODULE НашиРесурсы;
   IMPORT Телефония, СекретариВариантЭкстра;
   VAR
      телефония-: Телефония.Реализация;
      секретари-: Секретари.Реализация;

   PROCEDURE Иниц;
   BEGIN
      телефония := Телефония.стандартная;
      секретари := СекретариВариантЭкстра.реализация;
   END Иниц;

BEGIN
   Иниц
END НашиРесурсы;

Наконец, есть наш прикладной модуль:

MODULE НашОфис;
   IMPORT Телефония, Секретари, НашиРесурсы;

   VAR
      телЛиния: Телефония.Линия;
      секретарь1, секретарь2: Секретари.Секретарь;
   
   PROCEDURE Иниц;
   BEGIN
      телЛиния := НашиРесурсы.телефония.НоваяЛиния();
      секретарь1 := НашиРесуры.секретари.НовыйСекретарь();
      секретарь2 := НашиРесурсы.секретари.НовыйСекретарь();
      секретарь1.СестьНаТелефон(телЛиния);
      секретарь2.Помогать(секретарь1)
   END Иниц;

END НашОфис.

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

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