Почему в некоторых языках программирования отказываются от поддержки множественного наследования
Перейти к содержимому

Почему в некоторых языках программирования отказываются от поддержки множественного наследования

  • автор:

Почему множественное наследование не поддерживается в большинстве языков программирования?

Почему множественное наследование не поддерживается в большинстве языков программирования?

Я действительно мог бы использовать эту функцию для разработки другого макета приложения?

задан 16 мая ’10, 09:05

Многие языки поддерживают его за счет реализации нескольких интерфейсов, как и PHP. Не могли бы вы объяснить, что вы пытаетесь сделать? — Bart Kiers

Я комбинирую различные классы и создаю один основной класс, через который у меня будет доступ ко всем методам во всех подклассах. Моя библиотека кода очень большая, поэтому я пытался ее разделить. — Starx

хе-хе — думаю, тогда ответ очевиден, причина, по которой большинство языков не поддерживает его, заключается в том, что программисты предпочитают делать такие вещи ^^^. Посмотрите принцип единой ответственности. Я бы посоветовал вам удалить ВСЕ ваше наследство в качестве практики и просто использовать интерфейсы. Поработав некоторое время, несколько случаев, когда действительно следует использовать наследование, должны стать очевидными. Также помните, что каждый класс должен быть крошечным и делать только одну очень простую вещь. — Bill K

@ Билл К., я прошел долгий путь с тех пор, как задал этот вопрос, теперь я почти уверен, что мне делать. Спасибо за советы. — Starx

3 ответы

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

Одна проблема возникает, когда два родительских класса имеют элементы данных или методы с одинаковыми именами. Трудно решить, на что ссылается подкласс.

Другой случай возникает, когда два родительских класса наследуются от одного и того же базового класса, образуя «ромб» в иерархии наследования.

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

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

Некоторые языки пытаются обеспечить автоматическую объектно-реляционную модель, поэтому объекты можно сделать постоянными с помощью обычной RDMS. Это сопоставление было трудным в лучшие времена (оно было описано как «Вьетнамская война» разработки программного обеспечения), но гораздо сложнее, если поддерживается множественное наследование.

Почему множественное наследование не разрешено в Java или C #?

Я знаю, что множественное наследование недопустимо в Java и С#. Многие книги просто говорят, что множественное наследование не допускается. Но это может быть реализовано с помощью интерфейсов. Ничего не говорится о том, почему это запрещено. Кто-нибудь может сказать мне, почему это запрещено?

16 ответов

Короткий ответ: потому что разработчики языка решили не делать этого.

В принципе, казалось, что и разработчики .NET, и Java не допускают множественного наследования, потому что они полагают, что добавление MI добавило слишком много сложности к языкам, при этом слишком мало пользы.

Для более интересного и углубленного чтения есть некоторые статьи, доступные в Интернете с интервью с некоторыми из разработчиков языка. Например, для .NET Крис Брумме (который работал в MS на CLR) объяснил причины, по которым они решили не:

  • Различные языки действительно имеют разные ожидания относительно того, как MI работает. Например, как конфликты разрешено и дублируются ли дублирующие базы объединены или избыточны. Прежде чем мы сможем реализовать MI в CLR, мы должны сделать обзор всех языков, рисунок выработать общие концепции и решить как выразить их в языковой нейтральной манере. Мы также должны решить, принадлежит ли МИ CLS, и что это означало бы для языки, которые не хотят этой концепции (предположительно, например, VB.NET). из Конечно, бизнес, в котором мы находимся как общий язык выполнения, но мы не добрались до этого для МИ пока.

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

  • Наследование множественной реализации вносит много сложностей в реализация. Эта сложность удары, макет, отправка, доступ к полям, сериализация, идентификация сравнения, проверяемости, отражения, дженериков и, возможно, много других мест.

Для Java вы можете прочитать в этой статье:

Причины отказа нескольких наследование с языка Java в основном вытекают из «простого объекта» ориентированной и знакомой «цели. простой язык, создатели Java хотел, чтобы большинство разработчиков может захватывать без обширных обучение. С этой целью они сделать язык похожим на С++ как возможно (знакомый) без переноса над С++ ненужная сложность (просто).

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

17.9 – Множественное наследование

До сих пор все представленные нами примеры наследования были одиночными, то есть каждый наследующий класс имеет одного и только одного родителя. Однако C++ предоставляет возможность множественного наследования. Множественное наследование позволяет производному классу наследовать члены более чем от одного родителя.

Допустим, мы хотим написать программу, чтобы отслеживать группу преподавателей. Преподаватель – это человек. Однако преподаватель также является сотрудником (и, если он работает на себя, является работодателем). Множественное наследование можно использовать для создания класса Teacher (преподаватель), который наследует свойства как от Person (человек), так и от Employee (сотрудник). Чтобы использовать множественное наследование, просто укажите все базовые классы (как и в одиночном наследовании), разделив их запятыми.

Рисунок 1 Диаграмма наследования Рисунок 1 – Диаграмма наследования

Проблемы с множественным наследованием

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

Во-первых, неоднозначность может возникнуть, если несколько базовых классов содержат функцию с одним и тем же именем. Например:

Когда c54G.getID() компилируется, компилятор проверяет, содержит ли WirelessAdapter функцию с именем getID() . Ее у него нет. Затем компилятор проверяет, есть ли в каком-либо из родительских классов функция с именем getID() . Видите здесь проблему? Проблема в том, что c54G на самом деле содержит ДВЕ функции getID() : одна унаследована от USBDevice , а другая – от NetworkDevice . Следовательно, этот вызов функции неоднозначен, и вы получите ошибку компиляции, если попытаетесь скомпилировать этот код.

Однако есть способ обойти эту проблему: вы можете явно указать, какую версию вы хотели вызвать:

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

Во-вторых, более серьезная проблема – проблема ромба (или англоязычный термин – diamond problem). Она появляется, когда класс множественно наследуется от двух классов, каждый из которых наследуется от одного базового класса. Это приводит к ромбовидной структуре наследования.

Например, рассмотрим следующий набор классов:

Рисунок 2 Ромбовидная структура наследования Рисунок 2 – Ромбовидная структура наследования

Сканеры ( Scanner ) и принтеры ( Printer ) являются устройствами с питанием, поэтому они являются производными от PoweredDevice . Однако копировальный аппарат ( Copier ) включает в себя функции как сканеров, так и принтеров.

В этом контексте возникает много вопросов, в том числе, должен ли Copier иметь одну или две копии PoweredDevice , и как разрешать определенные типы неоднозначных ссылок. Хотя большинство этих проблем можно решить с помощью явного определения области видимости, затраты на поддержку, добавленные к вашим классам, чтобы справиться с дополнительной сложностью, могут привести к резкому увеличению времени разработки. Подробнее о способах решения проблемы ромба мы поговорим в следующей главе (урок «18.8 – Виртуальные базовые классы»).

Множественное наследование – больше проблем, чем оно того стоит?

Как оказалось, большинство задач, которые можно решить с помощью множественного наследования, можно решить и с помощью одиночного наследования. Многие объектно-ориентированные языки (например, Smalltalk, PHP) даже не поддерживают множественное наследование. Многие относительно современные языки, такие как Java и C#, ограничивают классы одним наследованием от обычных классов, но допускают множественное наследование от классов интерфейсов (о чем мы поговорим позже). Основная идея запрета множественного наследования в этих языках заключается в том, что это просто делает язык слишком сложным и в конечном итоге вызывает больше проблем, чем устраняет.

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

Интересно отметить, что вы уже использовали классы, написанные с использованием множественного наследования, даже не подозревая об этом: объекты библиотеки iostream std::cin и std::cout реализованы с использованием множественного наследования!

Правило

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

Кофе-брейк #95. Как решить проблему множественного наследования в Java

Кофе-брейк #95. Как решить проблему множественного наследования в Java - 1

Источник: FreeCodeCamp Java — один из самых популярных объектно-ориентированных языков программирования, используемых сегодня. Поскольку он не зависит от платформы, вы можете найти Java-приложения на всех типах устройств и в каждой операционной системе. А поскольку Java относительно легко выучить, это один из языков, который осваивают многие программисты. Важная особенность Java, с которой вы должны быть знакомы, — это наследование классов. Наследование позволяет оптимизировать код, облегчая повторное использование классов. Когда вы можете повторно использовать код, который уже был протестирован и отлажен, жизненный цикл разработки программного обеспечения становится короче и менее затратным. Хотя теоретически это простая концепция, кодирование отношений наследования требует внимания к деталям. Особенно в отношении множественного наследования, когда один дочерний класс наследует свойства от нескольких родительских классов. Java отвергает множественные отношения наследования, потому что они создают неоднозначность, но есть несколько способов добиться многих из тех же эффектов, если вы знаете, что делать. В этой статье мы рассмотрим проблемы с множественным наследованием и обсудим альтернативные варианты кодирования на Java.

Терминология наследования

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

Типы наследования ООП

  • Одноуровневое наследование : когда дочерний класс наследует функции от единственного родительского класса.
  • Многоуровневое наследование : это многоуровневая форма одноуровневого наследования. При многоуровневом наследовании дочерний класс также может выступать в качестве родительского класса для других дочерних классов. Отношения между каждым уровнем линейны — никакие ветви не выходят выше, чем при множественном наследовании. В этом случае конечный дочерний класс имеет функции со всех уровней выше.
  • Иерархическое наследование : противоположность множественного наследования. В иерархическом наследовании единственный родительский класс имеет более одного дочернего класса. Таким образом, вместо того, чтобы иметь ветви над ним, он разветвляется внизу.
  • Гибридное наследование : как следует из названия, гибридное наследование представляет собой комбинацию других типов наследования.
  • Множественное наследование : при множественном наследовании дочерний класс имеет более одного родительского класса. Хотя Java и JavaScript не поддерживают множественное наследование, такие языки ООП, как C ++, поддерживают.
  • Многопутевое наследование : гибрид множественного, многоуровневого и иерархического наследования, при многопутевом наследовании дочерний класс наследует свои характеристики и функции от родительского класса и нескольких дочерних классов родительского класса. Поскольку многопутевое наследование основано на множественном наследовании, Java не поддерживает его использование.

Почему Java не поддерживает множественное наследование

Кофе-брейк #95. Как решить проблему множественного наследования в Java - 3

Основная проблема множественного наследования заключается в том, что оно может создавать неоднозначности в дочерних классах. В обзорном техническом документе 1995 года ведущий дизайнер Java Джеймс Гослинг заявил, что проблемы с множественным наследованием были одной из причин создания Java. Сложности, присущие множественному наследованию, наиболее отчетливо видны в проблеме алмаза. В задаче “ромб” родительский класс A имеет два различных дочерних класса B и C; то есть дочерние классы B и C расширяют класс A. Теперь мы создаем новый дочерний класс D, который расширяет как класс B, так и класс C. Обратите внимание, что у нас есть множественное наследование (D расширяет B и C), иерархическое наследование (B и C расширяют A) и многоуровневое наследование (D расширяет A, B и C). В проблеме ромба дочерние классы B и C наследуют метод от родительского класса A. И B, и C переопределяют унаследованный метод. Но новые методы в B и C противоречат друг другу. Окончательный дочерний класс D наследует два независимых и конфликтующих метода от своих нескольких родителей B и C. Неясно, какой метод класса D следует использовать, поэтому возникает двусмысленность. Другие языки программирования ООП реализуют различные методы решения неоднозначности множественного наследования.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *