Как избавиться от множественного наследования?

 
1 2 3
+
-
edit
 

Balancer

администратор
★★★★★
Вот не понимаю я паранойи последнего времени на тему избавления новых языков от множественного наследования.

Вот уткнулся сейчас в проблему вообще в банальном PHP...

Задача - есть набор классов (с большим набором методов) тех или иных web/db-ресурсов. Это может быть простой ORM-маппинг, может быть дерево, хитро извлекаемое из БД, список...

Их наследники - конкретные типы объектов. Грубо говоря - простая страничка сайта, или вывод сложной формы...

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

Скажем, раздел фирм на сайте, но в нём могут быть простые страницы, деревья категорий, списки регионов и т.п.

Пока нам хватает стандартных настроек - всё прекрасно.

Но вот нам для раздела сайта требуется ряд общих настроек. Например, идентификаторы баннеров, имя шаблона...

Если все виды страниц относятся к одному классу - опять прекрасно. Делаем промежуточный класс, где всё это прописываем, а уже от него наследуем наши страницы.

Но что делать, когда страницы должны принадлежать разным базовым классам??

Будь у нас множественное наследование - никаких проблем. Скажем, дерево категорий наследуем от базового дерева и базового набора свойств раздела.

А вот без него - фиг.

Каким боком тут можно прикрутить те же интерфейсы, скажем - просто не представляю.

Как быть-то?
 
+
-
edit
 

timochka

опытный

Рома, может я чего-то не понял из твоего поста.
Но избавляться пытаются от множественного наследования классов, но не интерфейсов.
Т.е. базовых классов всегда не больше одного, а вот интерфейсов которые ты в классе имплементишь может быть много.
Т.е. у тебя есть интерфейс IHierarchy (дерево), есть интерфейс IForm (просто форма), есть интерфейс IElementList (список чего-нибудь).
Есть базовый класс Page который представляет из себя базовую реализацию страницы.
Ты делаешь свой класс страницы наследуя его от Page и интерфейсов IHierarchy и IForm.
И дело сделано.
Если ты хочешь использовать уже готовыю реализацию интерфейса IHierarchy из другого класса - то делаешь агрегацию.
 
+
-
edit
 

Balancer

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

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

Один - собственно, основная работа класса (данные страницы, заголовок и т.п.)

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

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

К сожалению, это очень частный случай.

Композиция вместо наследования тут не спасёт. Потому что, придётся делать обёртки для каждого метода из композиции, а таких методов - десятки. При чём все почти - из одной-двух строк, так что эти обёртки - тупо в полтора-два раза увеличат размер кода без какой-либо эффективной отдачи.

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

В общем, куда ни кинь - одни костыли выходят.

И ради чего? Ради отказа от естественного представления объектов в окружающем мире.

Яблоко - это растение, объект зелёного цвета, пища.

Звезда - это астрономический объект, природный термоядерный реактор, центр планетарной системы...

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

И ООП, которое изначально сделало шаг в этом направлении, потом за совершенно неадекватными оговорками делает шаг назад и начинает вводить костыли в виде интерфейсов, композиций и т.п....

Главный (и единственный?) аргумент против множественного наследования - неоднозначности в случае появления одинаковых методов у разных родителей.

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

Так откуда тогда могут взяться неоднозначности?

Мне, всё же кажется, что тут у народа какой-то массовый заскок.
 
+
-
edit
 

Balancer

администратор
★★★★★
Возможно, в отрыве от практики задача выглядит непонятно, постараюсь показать (упрощённо) ключевые моменты кода:

1. Есть базовый класс "страница сайта", содержащий массу методов, возвращающих параметры страницы (из которых система потом и собирает эту страницу):
title() - заголовок страницы
description() - её описание
source() - текст
body() - скомпилированный (разметка -> html) текст, по умолчанию сам собирается из source()
create_time() - дата создания
modify_time() - дата изменения
и т.д. и т.п.

У базовой страницы методы в основном абстрактные, но у неё куча наследников. Страницы, которые извлекают данные из файлов файловой системы, страницы, которые извлекают данные и базы данныех и т.п.

Извлечение из БД может идти тоже разными способами, это могут быть простые плоские данные, когда текст страницы берётся из некой таблицы, скажем, текст постинга на форуме по его ID. Может быть построение дерева из данных дерева категорий, заданного в виде графа, могут быть списки и т.п.

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

В случае множественного наследования проблем нет. Скажем, наш список наследует классы def_dbtree и my_section и в нём пишется, подчас, буквально несколько строк кода. Запрос, генерирующий данные для дерева (само дерево соберётся методами из def_dbtree, которые будут делать этот запрос), небольшая подстройка данных по умолчанию в my_section (скажем, у всех страниц единый ID баннера в баннерной сети, но для отдельного класса страниц он должен быть иной).

Но у нас множественного наследования нет.

Что можно предложить взамен?

Композиция отпадает сразу. Потребуются обёртки для многих десятков параметров.

Копипаст?

Мой вариант с вторичным инициализатором в конкретных условиях?

Костыли, одни костыли...
 
UA timochka #24.07.2007 14:59  @Balancer#24.07.2007 13:44
+
-
edit
 

timochka

опытный

Balancer> Композиция вместо наследования тут не спасёт. Потому что, придётся делать обёртки для каждого метода из композиции, а таких методов - десятки. При чём все почти - из одной-двух строк, так что эти обёртки - тупо в полтора-два раза увеличат размер кода без какой-либо эффективной отдачи.

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

Balancer> Куда ни кинь, все объекты - продукты множественного наследования.
Balancer> И ООП, которое изначально сделало шаг в этом направлении, потом за совершенно неадекватными оговорками делает шаг назад и начинает вводить костыли в виде интерфейсов, композиций и т.п....
Balancer> Главный (и единственный?) аргумент против множественного наследования - неоднозначности в случае появления одинаковых методов у разных родителей.
Balancer> Мне, всё же кажется, что тут у народа какой-то массовый заскок.

Ты смотришь на проблему с позиции разрабочика-одиночки. Сейчас развитие идет в сторону групповой разработки. Проблема состоит в том что ядро системы разрабатывается небольшим количеством крутых профессионалов, а потом для поддержки и использования должно быть достаточно средней квалификации девелоперов. Поэтому выработались следующие принципы:
1) Для влезания в середину существующего кода желательно изучать как можно меньше. Т.е. разлишные модули должны быть слабо связанны.
2) Для того что-бы переиспользовать существующую реализацию нельзя требовать изучения толстого унаследованного API (крайне желательно перечислить большинство методов объекта прямо в классе реализации).
3) У множественного наследования есть еще проблема с многократным наследованием реализации (и данных) одного базового класса от нескольких родителей. И все эти трюки с виртуальным наследованием не есть хорошо, потому что затрудняют понимание сторонним человеком принципов работы кода.

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

Хороший пример .Net Framework - старались сделать небольшую глубину наследования и все равно для классов GUI тяжело охватить все методы одного класса мысленным взором.

Поэтому считают что лучше явно прописать все методы в классе при агрегации, чем полагаться на унаследованную реализацию. А совпадение имен методов в разных классах (если их пишут разные люди) случаются очень часто.

Я понимаю, что для тебя это большое количество тупой обезьяньей работы. Но в коллективе таких обезьян всегда много, а толковых девелоперов нехватает.
 
Это сообщение редактировалось 24.07.2007 в 15:04
RU Balancer #24.07.2007 15:19  @timochka#24.07.2007 14:59
+
-
edit
 

Balancer

администратор
★★★★★
timochka> Рома, объем кода давно никого не пугает, пугает сложность. Т.е. много простого кода лучше чем мало сложного.

Меня пугает. Если мне для реализации класса, который в ином случае реализуется в 10 строк, придётся писать 100 строк, то на весь проект вместо месяца работы мне понадобится год. А та же Авиабаза в нынешнем варианте потребовала бы уже не 10 лет экспериментов, а 100 лет :D

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

В противном случае до сих пор бы все программировали в машинных кодах :D

timochka> Ты смотришь на проблему с позиции разрабочика-одиночки. Сейчас развитие идет в сторону групповой разработки.

Видишь ли, я как раз уже нарывался несколько на эти проблемы именно в групповой разработке на Java :) Когда вместо простого и наглядного решения через множественное наследование приходилось добавлять никому непонятные костыли.

timochka> 1) Для влезания в середину существующего кода желательно изучать как можно меньше. Т.е. разлишные модули должны быть слабо связанны.

Множественное наследование этому никак не мешает.

timochka> 2) Для того что-бы переиспользовать существующую реализацию нельзя требовать изучения толстого унаследованного API (крайне желательно перечислить большинство методов объекта прямо в классе реализации).

Опять же, со множественным наследованием это никак не связано. Ну выдаст тебе компилятор ошибку одноимённых методов, ну избавишься ты от их использования или прямо укажешь родителя.

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

Почему для имён классов такой подход допустим, а для имён методов в случае множественного наследования - нет?

timochka> 3) У множественного наследования есть еще проблема с многократным наследованием реализации (и данных) одного базового класса от нескольких родителей

Не понял проблемы. Если у кого-то из родителей в цепочке метод переопределён - это будет другой метод с тем же именем и компилятор прекрасно отловит неоднозначность.

timochka> В большом коллективе 80% работы это задачи типа есть неправильное поведение в таком-то случае.

Компилятор способен в 100% случаев отловить проблемы множественного раследования. Как он отлавливает те же проблемы со "множественным импортом".

Поэтому неожиданного поведения тут просто не может быть. Или программа не соберётся, или соберётся, но с явным знанием компилятора о том, какой метод вызывался.

timochka> Поэтому считают что лучше явно прописать все методы в классе при агрегации, чем полагаться на унаследованную реализацию.

При пропагандируемом сейчас принципе максимального рефакторинга получается полнейший бред. Приходится писать в 10 раз больше кода, при этом в 10 раз возрастает риск пропустить ошибку. Ежу понятно, что любой типичный программист начинает делать копипаст. И вместо:
code cpp
  1. int modify_time() { return parent2->modify_time(); }
  2. int create_time() { return parent2->create_time(); }


мы получим:

code cpp
  1. int modify_time() { return parent2->modify_time(); }
  2. int create_time() { return parent2->modify_time(); }


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

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

Murkt

Pythoneer

Переписывай на Питон ;) Тут множественное наследование есть. Правда, компилятор не отловит ошибок на этапе компиляции, но зато можно динамически унаследоваться от чего угодно :)
[team Їжачки - сумні падлюки]  
+
-
edit
 

Balancer

администратор
★★★★★
Скорее всего, в будущем система и будет на Питоне.

Но будет это не раньше, чем Питон массово появится на виртуальных хостингах :) Пока же это, ИМХО, в принципе невозможно. На сколько я знаю, никакая из реализаций связки Питон + веб-сервер не обеспеивает per-user виртуального хостинга :-/
 
UA timochka #24.07.2007 15:56  @Balancer#24.07.2007 15:19
+
-
edit
 

timochka

опытный

timochka>> Рома, объем кода давно никого не пугает, пугает сложность. Т.е. много простого кода лучше чем мало сложного.
Balancer> Как раз объём кода (утрированно, естественно) - это то, за чем упорно гонится весь программистский мир за последние лет 30 :) Каждое повышение уровня языка заключается в том, чтобы как можно меньшими усилиями реализовать как можно больший функционал. Одно из важных требований для осуществления этой задачи - как раз повышение функциональной насыщенности кода.

Не совсем - борьба идет за более простой и понятный большинству код. А обьем?
Прошлый проект 70 Mb Java sources, 50 Mb Cpp sources, не считаю сторонних библиотек, темплейтов и картинок. Т.е. чисто текстовые файлы. Это сделано за 2 года. Если тебе для исправление бага не нужно изучать все - это нормально.

Balancer> В противном случае до сих пор бы все программировали в машинных кодах :D

Они сильно непонятные. Нанять десяток человек способных писать большую систему в кодах очень тяжело.

timochka>> 1) Для влезания в середину существующего кода желательно изучать как можно меньше. Т.е. разлишные модули должны быть слабо связанны.
Balancer> Множественное наследование этому никак не мешает.

Оно добавляет связи между классами которые до этого работали независимо.

timochka>> 2) Для того что-бы переиспользовать существующую реализацию нельзя требовать изучения толстого унаследованного API (крайне желательно перечислить большинство методов объекта прямо в классе реализации).
Balancer> Опять же, со множественным наследованием это никак не связано. Ну выдаст тебе компилятор ошибку одноимённых методов, ну избавишься ты от их использования или прямо укажешь родителя.

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

Balancer> Почему для имён классов такой подход допустим, а для имён методов в случае множественного наследования - нет?

Ром, проблема не в том что ты не помнишь имен точно. А в том что ты не понимаешь как работает смесь двух классов. И чем больше кода туда замесили (через наследование) тем труднее понять.

timochka>> 3) У множественного наследования есть еще проблема с многократным наследованием реализации (и данных) одного базового класса от нескольких родителей
Balancer> Не понял проблемы. Если у кого-то из родителей в цепочке метод переопределён - это будет другой метод с тем же именем и компилятор прекрасно отловит неоднозначность.

Классы это еще и данные. Если у тебя есть класс А, класс Б унаследован от А, класс В унаследован от А, класс Г унаследован от Б и В.
Г имеет 2 копии данных полученых от класа А. Ты должен явно указывать с какими данными ты работаешь (помня всю цепочку наследования). И если хоть раз ошибешься (типа аллоцировал ресурсы в одном экземпляре А, а освободил в другом), то компилятор промолчит, а программа вылетит на исполнении.

timochka>> В большом коллективе 80% работы это задачи типа есть неправильное поведение в таком-то случае.
Balancer> Компилятор способен в 100% случаев отловить проблемы множественного раследования. Как он отлавливает те же проблемы со "множественным импортом".

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

Balancer> Поэтому неожиданного поведения тут просто не может быть. Или программа не соберётся, или соберётся, но с явным знанием компилятора о том, какой метод вызывался.
Она может не собираться месяцами, потому что команда будет делать ошибки быстрее чем исправлять.

timochka>> Поэтому считают что лучше явно прописать все методы в классе при агрегации, чем полагаться на унаследованную реализацию.
Balancer> При пропагандируемом сейчас принципе максимального рефакторинга получается полнейший бред. Приходится писать в 10 раз больше кода, при этом в 10 раз возрастает риск пропустить ошибку.

Считается, что ошибки не от опечаток (это проверит компилятор), а от непонимания работы класса. 10 раз переписал реализацию - в 3 раза лучше понял работу класса.

Ром, в качестве примера сложной библиотеки STL. Попробуй там разобраться и исправить реализацию одного класса (из середины иерархии). А потом прикинь - на сколько сильно множественное наследование усложняет понимание библиотеки.
 
RU Balancer #24.07.2007 16:01  @timochka#24.07.2007 15:56
+
-
edit
 

Balancer

администратор
★★★★★
timochka> Считается, что ошибки не от опечаток (это проверит компилятор)

В том-то и дело, что указанный мной пример - это не опечатка. И компилятор такое пропустит легко. И работать оно будет. Вот только работать - не так, как ожидается. Сколько времени потом такую ошибку ловить?

В то время как при множественном наследовании ошибку неоднозначности компилятор отловит.
 

Murkt

Pythoneer

2timochka
В Питоне множественное наследование обычно используется в качестве микс-инов. Скажем так - я ещё ни разу не видел, чтоб его применяли в каком-то другом случае. Очень хорошая штука, когда мне нужно добавить к какому-нибудь классу из сторонней библиотеки несколько функций - я пишу свой класс, и добавляю его к предкам библиотечного - voila, всё работает. Я так понимаю, что Балансеру сейчас нужно как раз что-то подобное.
[team Їжачки - сумні падлюки]  
24.07.2007 16:21, Balancer: +1: Давно пора :)
US Сергей-4030 #24.07.2007 18:35  @Balancer#24.07.2007 13:44
+
-
edit
 

Сергей-4030

исключающий третье
★★
админ. бан
Balancer> И ради чего? Ради отказа от естественного представления объектов в окружающем мире.
Balancer> Яблоко - это растение, объект зелёного цвета, пища.

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

Balancer> Главный (и единственный?) аргумент против множественного наследования - неоднозначности в случае появления одинаковых методов у разных родителей.

И не главный и не единственный. Главный - то, что на практике "двухмоторный самолет" становится наследником от двух моторов, пропеллера, двух крыльев и хвоста. И далее начинается кошмар. Причем у самых что ни есть квалифицированных разработчиков.

Balancer> Композиция вместо наследования тут не спасёт. Потому что, придётся делать обёртки для каждого метода из композиции, а таких методов - десятки. При чём все почти - из одной-двух строк, так что эти обёртки - тупо в полтора-два раза увеличат размер кода без какой-либо эффективной отдачи.

Почему это? Кто сказал, что "обертывать" нужно таким способом?

Balancer> "Инверсия логики" класса, т.е. рассматривать родителем не источник данных, а нужную конфигурацию, а источник данных композировать в виде стороннего механизма источника этих данных тоже некрасиво, так как удваивается количество классов. Всё равно придётся писать класс-источник данных, но вдобавок - ещё и класс-заглушку, наследник текущего представления для вывода.

Ничего подобного, с какой стати? ОК, есть обьект "страничка". Иногда статическая. Иногда динамическая, ис БД. ОК, наша страничка имеет ссылкы на DataProvider . Где ты увидел удвоение классов? Что такое "текужее представление"?
 
RU Balancer #24.07.2007 21:52  @Сергей-4030#24.07.2007 18:35
+
-
edit
 

Balancer

администратор
★★★★★
Сергей-4030> Ничего подобного, с какой стати? ОК, есть обьект "страничка". Иногда статическая. Иногда динамическая, ис БД. ОК, наша страничка имеет ссылкы на DataProvider . Где ты увидел удвоение классов?

Ну вот. У меня есть наворочанная система, использующая прямые вызовы. Да, идея с DataProvider в голову приходила. Но:
- Есть тонны кода, который придётся тупо переписывать.
- Получаем заметное падение производительности, поскольку у нас появляется лишняя операция извлечения этого DataProvider.
- Наконец, придётся переписать всю систему шаблонов, так как используемая не понимает метода от метода для класса. Ну вот такие у Smarty ограничения.

Итого - имеем человекомесяц лишней работы... Да проще уж, действительно, всё на Python переписать, где это множественное наследование есть :D Объём работы сопоставим будет. И перспективы, опять же...

Сергей-4030> Что такое "текужее представление"?

Шаблоны, элементы навигации и т.п. И вместо одного класса "страница такого-то вида" придётся делать два: "дата-провайдер такого-то вида" и "раздел сайта такого-то вида, использующий такой-то дата-провайдер".
 
US Сергей-4030 #24.07.2007 22:50
+
-
edit
 

Сергей-4030

исключающий третье
★★
админ. бан
Balancer> - Есть тонны кода, который придётся тупо переписывать.

It is irrelevant to the topic. You asked why multiple inheritance is bad (more exact, why new languages get rid of that), not how to refactor your project.

Balancer> - Получаем заметное падение производительности, поскольку у нас появляется лишняя операция извлечения этого DataProvider.

Nothing even close to this. What are you talking about? Do you put your JDBC operators to one method? All those methods should be separated. This is business logic, it should be clearly separated from the interface. If we are talking about the problem you described, the best way to do this would be put all business logic apart, to EJB methods. If you think it would be not fast enough, think - financial institutes do it this way for millions of transactions and it's good enough for them.

Balancer> - Наконец, придётся переписать всю систему шаблонов, так как используемая не понимает метода от метода для класса. Ну вот такие у Smarty ограничения.

Irrelevant to the topic.

Balancer> Итого - имеем человекомесяц лишней работы... Да проще уж, действительно, всё на Python переписать, где это множественное наследование есть :D Объём работы сопоставим будет. И перспективы, опять же...

It depends on what you want. It cannot prove that multiple inheritance is a good approach to design.

PS Sorry for English, don't have Russian keyboard. No need to answer English, though, I can read Russian. ;)
 
Это сообщение редактировалось 25.07.2007 в 00:02
+
-
edit
 

Balancer

администратор
★★★★★
А, по-моему, из исходной постановки очевидно, что не просто "Как избавиться от множественного наследования?", а "Как избавиться от множественного наследования малой кровью?".

Ибо с тем или иным количеством костылей можно избавиться от всего.
 
+
-
edit
 

Mishka

модератор
★★★

Я приду домой и напишу большой пост. :F На работе нет времени. Попробую порассуждать на всё темы сразу.
 
US Сергей-4030 #25.07.2007 02:11  @Balancer#25.07.2007 00:27
+
-
edit
 

Сергей-4030

исключающий третье
★★
админ. бан
Balancer> А, по-моему, из исходной постановки очевидно, что не просто "Как избавиться от множественного наследования?", а "Как избавиться от множественного наследования малой кровью?".

Ну, значит, я не понял. При этом идею явно отделить бизнес-методы, как мне кажется, стоит реализовать в любом случае рефакторинга.
 
RU riven-mage #25.07.2007 02:33  @Balancer#24.07.2007 13:44
+
-
edit
 

riven-mage

опытный

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

multiple inheritance = single inheritance + multiple interfaces + mixins

То есть более явно прописывается в коде наследование данных, наследование набора методов и наследование (или шаблоны) реализации методов.
What can change the nature of a man ?  
Это сообщение редактировалось 25.07.2007 в 03:29
+
-
edit
 

Mishka

модератор
★★★

Несколько мыслей.

Множественное наследование, простое наследование. На примере С++.

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

Так появилось множественное наследование. Логичное продолжение простого. А полиморфизм просто засиял — ведь объект мог быть всем, что было в базе. Проблемы начались, когда стали думать об эффективной реализации. Не выходил каменный цветок. Точнее выходил, но не такой. Для виртуальных методов(статические методы и name mangling пока не трогаем — это не совсем реальный полиморфизм — это статический полиморфизм). Это было понятно — просто таблицей адресов методов было не обойтись (в этом смысле ООП было ещё в форме файлов значительно раньше — когда для открытого файла можно было назначить свои процедуры обработки конца файла, конца страницы и т.д.). Приходилось тащить целую кучу таблиц. Что выливалось в понижении эффективности. Ну тут начались и те проблемы, что многие таблицы, методы и члены могли включатся много раз. Разрешить? Запретить? Так появились виртуальные базовые классы.

Но множественное наследование — это статическое дело. И применять его для решения некоторых динамических задач можно, но не нужно. Но поезд не остановить. Особенно пяткой. Народ стал использовать. Что послужило одним из толчков к разработке паттернов в программировании. Банда четырёх проанализировала код и вывела 25 патернов. И пошло и поехало. Действительно, оказалось, что можно и нужно использовать аггрегирование и композицию вместо множественного наследования. И это было правильно. Джава создатель решил, что множественное наследование не нужно в таком виде. Но отказаться от всех преимуществ полиморфизма в множественном наследовании не хотелось. Так появились интерфейсы. Но...интерфейс приходится имплементировать каждый раз заново. Т.е. интерфейс есть, а наполнения — ноль. Вот она — цена счастья. Но иногда хочется тянуть методы и алгоритмы при том, что типы членов не известны. В С++ есть templates (Степанов занимался generic programьing ещё в 70-е годы — АТП, Ада — generic packages, другие языки). GP в чистом виде код без данных — полная абстракция от типов данных. Ну и, как результат, статический полиморфизм за счёт генерации кода. Правда, в С++ ввели специализацию для уменьшения того, что называют code bloating. А в Джаву новую добавили templates.

Поэтому я не совсем согласен с riven-mage — множественное наследование изменилось. И сильно. А вот как результат пришлось в некоторые языки вводить небольшие костыльки. multiple interfaces не есть полноценная замена множественного наследования — наполнение интерфейса приходится делать каждый раз заново. Но это и понятно — не получается полностью имплементировать интерфейс, если не известен объект, для которого надо его имплементировать. Так сказать чисто теоритический залепон. Темплэйты единственный выход. Плата за это — приходится таскаться по дереву наследования на несколько уровней выше, а вниз — даункастом. Что очень не кошерно.

С точки зрения вышесказанного — дизайн, что у Ромы требует полного рефакторинга. Не ложаться странички сервера нормально на множественное наследование. Композиции, агрегации и т.д. нужны. Тут я Сергеем и Тимочкой соглашусь на 100%.

Как переделать малой кровью? Темплэётами, если они есть. Если нет — рефакторить. Со сменой дизайна.
 
25.07.2007 12:42, Murkt: +1: Хорошо расписано.
+
-
edit
 

timochka

опытный

Mishka> Несколько мыслей.
Гладко излагаешь!!!!

По поводу интерфейсов - сильно нехватает такой штуки:
Класс объявлет, что он реализует интерфейс. Реализации естественно нету, но есть некий базовый имплементатор этого интерфейса. Желательно как-то использовать базовую имплементацию с возможностью что-то настроить.

Сейчас ты просто аггрегируешь имплементатор и пишешь кучку кода. Где-то есть скрипты (snippets) которые сгенерируют тебе код автоматом, а ты потом поправишь то что тебе не нравиться.
Но хочется большей гибкости. Как-то стандартизировать эти скрипты. Сделать что-то типа шаблонов с параметрами, но что-бы код генерировался до компиляции и был доступен для правки.
 

Murkt

Pythoneer

Лиспу хотите?
[team Їжачки - сумні падлюки]  
RU riven-mage #26.07.2007 13:21  @timochka#26.07.2007 09:19
+
-
edit
 

riven-mage

опытный

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

Уже есть, если я правильно тебя понял :)

Template mixins
Templates

В будущем Уолтер Брайт грозится сделать еще и template inheritance :F
What can change the nature of a man ?  
US Mishka #27.07.2007 01:57  @riven-mage#26.07.2007 13:21
+
-
edit
 

Mishka

модератор
★★★

timochka>> Но хочется большей гибкости. Как-то стандартизировать эти скрипты. Сделать что-то типа шаблонов с параметрами, но что-бы код генерировался до компиляции и был доступен для правки.
riven-mage> Уже есть, если я правильно тебя понял :)

Ну, дык, интерфейсы в том виде, в каком они были в Джаве — ещё то уродство. Темплейты спасение. Иначе, как Рома упоминал — copy&paste. Или просто писание. А в С++ добавили специализацию под влиянием Степанова, если я правильно помню.

riven-mage> В будущем Уолтер Брайт грозится сделать еще и template inheritance :F

Ну так, в С++ есть. Значит будет и в других. :P
 
US Сергей-4030 #27.07.2007 07:16  @Mishka#27.07.2007 01:57
+
-
edit
 

Сергей-4030

исключающий третье
★★
админ. бан
Mishka> Ну, дык, интерфейсы в том виде, в каком они были в Джаве — ещё то уродство. Темплейты спасение. Иначе, как Рома упоминал — copy&paste. Или просто писание. А в С++ добавили специализацию под влиянием Степанова, если я правильно помню.

Почему? Мне, наоборот, очень нравятся. :( Именно интерфейсы, абстракция без имплементации. Помогает привести в порядок мозги. Вот в нашей новой конторе есть много таких приколов: что-то вроде:

class Interface {
public:
void setSomething() {
_impl->setSomething();
}
private:
ImplInterface *_impl;
}

class ImplInterface:public Interface() {
void setSomething() {
// blabla
}
}

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

А темплейты в Джаве, собственно, добавили исключительно чтоб избежать тайпкастов в контейнерах. И это, имхо, чуть ли не единственное их обоснованное применение. Причем, сугубое имхо, НЕ только в Джаве, а и в C++. Не люблю я ни перегружаемых операторов ни сложных темплейтов. ИМХО, от них зло. ;)
 
US Сергей-4030 #27.07.2007 07:39  @timochka#26.07.2007 09:19
+
-
edit
 

Сергей-4030

исключающий третье
★★
админ. бан
timochka> Класс объявлет, что он реализует интерфейс. Реализации естественно нету, но есть некий базовый имплементатор этого интерфейса. Желательно как-то использовать базовую имплементацию с возможностью что-то настроить.

Ну так дай доступ к этому имплементатору. Вместо:

class Самолет extends Планер implements Мотор {
public void датьГазу () {
мотор.датьГазу();
}
private мотор;
}

и потом

самолет.датьГазу();

пиши так:

class Самолет extends Планер {
public Мотор мотор () {
return мотор;
}
private мотор;
}

и потом

самолет.мотор().датьГазу();

В чем, собственно, проблема?
 
1 2 3

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru