Рассуждать о том, необходимо ли наследование (или какая-либо отдельная особенность на самом деле) или нет, без учета остальной семантики языка, бессмысленно; ты споришь в вакууме.
Что вам нужно, так это последовательная философия языкового дизайна; язык должен уметь элегантно решать проблемы, для которых он предназначен. Модель для достижения этого может требовать или не требовать наследования, но трудно судить об этом без общей картины.
Если, например, ваш язык имеет первоклассные функции, приложение с частичными функциями, полиморфные типы данных, переменные типов и универсальные типы, вы в значительной степени охватили те же основы, что и при классическом наследовании ООП, но с использованием другой парадигмы.
Если у вас поздняя привязка, динамическая типизация, методы как свойства, гибкие аргументы функций и первоклассные функции, вы также охватите те же основания, но опять же, используя другую парадигму.
(Поиск примеров для двух изложенных парадигм оставлен в качестве упражнения для читателя.)
Итак, подумайте о том, какая семантика вам нужна, поиграйте с ними и посмотрите, достаточны ли они без наследования. Если это не так, вы можете либо решить добавить наследование в микс, либо решить, что чего-то еще не хватает.
Да, это вполне разумное дизайнерское решение, исключающее наследование.
На самом деле есть очень веские причины для удаления наследования реализации, так как это может привести к созданию чрезвычайно сложного и сложного в обслуживании кода. Я бы даже зашел так далеко, что расценил наследование (как это обычно реализуется в большинстве языков ООП) как несоответствие.
Например, Clojure не обеспечивает наследование реализации, предпочитая предоставлять набор ортогональных функций (протоколы, данные, функции, макросы), которые можно использовать для достижения тех же результатов, но гораздо более чисто.
Вот видео, которое я нашел очень поучительным по этой общей теме, где Рич Хикки определяет основные источники сложности в языках программирования (включая наследование) и представляет альтернативы для каждого: Простота - это легко
источник
Когда я впервые столкнулся с тем фактом, что классы VB6 не поддерживают наследование (только интерфейсы), это действительно раздражало меня (и до сих пор поддерживает).
Однако причина , по которой он так плох, в том, что у него также не было параметров конструктора, поэтому вы не могли сделать обычное внедрение зависимостей (DI). Если у вас есть DI, то это важнее, чем наследование, потому что вы можете следовать принципу предпочтения композиции перед наследованием. В любом случае, это лучший способ повторно использовать код.
Не имея Mixins, хотя? Если вы хотите реализовать интерфейс и делегировать всю работу этого интерфейса объекту, установленному посредством внедрения зависимостей, тогда Mixins идеальны. В противном случае вы должны написать весь стандартный код, чтобы делегировать каждый метод и / или свойство дочернему объекту. Я делаю это много (спасибо C #), и это одна вещь, которую я хотел бы не делать.
источник
Чтобы не согласиться с другим ответом: нет, вы выбрасываете функции, когда они несовместимы с чем-то, что вы хотите больше. Java (и другие языки GC) отказались от явного управления памятью, потому что требовали большей безопасности типов. Хаскелл выбросил мутацию, потому что он хотел больше рациональных рассуждений и причудливых типов. Даже C отбросил (или объявил недопустимым) определенные виды псевдонимов и другое поведение, потому что он больше хотел оптимизации компилятора.
Итак, вопрос: что вы хотите больше, чем наследство?
источник
free
/delete
операция дает вам возможность аннулировать ссылки; если ваша система типов не в состоянии отследить все затронутые ссылки, это делает язык небезопасным. В частности, ни C, ни C ++ не являются безопасными по типу. Это правда, что вы можете пойти на компромиссы в одном или другом (например, линейные типы или ограничения размещения), чтобы заставить их согласиться. Чтобы быть точным, я должен был сказать, что Java хотела безопасность типов с определенной, простой системой типов и неограниченным распределением .null
ссылки). Запретfree
является довольно произвольным дополнительным ограничением. Таким образом, то, является ли язык безопасным типом, зависит больше от вашего определения безопасности типов, чем от языка.T
ссылка ссылается на одинnull
или на расширение классаT
;null
это некрасиво, но операции поnull
выбрасыванию четко определенного исключения, а не искажения инварианта типа выше. Контраст с C ++: после вызоваdelete
,T*
указатель может указывать на память , которая больше не удерживаетT
объект. Хуже того, выполняя назначение поля с использованием этого указателя в назначении, вы можете полностью обновить поле объекта другого класса, если оно случайно оказалось размещенным по соседнему адресу. Это не безопасность типов по любому полезному определению термина.Нет.
Если вы хотите удалить функцию базового языка, то вы повсеместно заявляете, что она никогда, никогда не нужна (или неоправданно трудна для реализации, что здесь не применимо). И «никогда» - сильное слово в разработке программного обеспечения. Вам понадобится очень веское обоснование, чтобы сделать такое заявление.
источник
Если говорить строго с точки зрения C ++, наследование полезно для двух основных целей:
Для пункта 1, если у вас есть какой-то способ поделиться кодом без чрезмерной акробатики, вы можете покончить с наследованием. Для пункта 2. Вы можете пойти по пути Java и настаивать на интерфейсе для реализации этой функции.
Преимущества удаления наследства
Компромисс в значительной степени находится между «Не повторяй себя» и гибкостью с одной стороны и предотвращением проблем с другой. Лично я не хотел бы видеть наследование от C ++ просто потому, что какой-то другой разработчик может быть недостаточно умен, чтобы предвидеть проблемы.
источник
Вы можете сделать много очень полезной работы без наследования реализации или миксинов, но мне интересно, нужно ли вам иметь какое-то наследование интерфейса, т. Е. Объявление, которое говорит, что если объект реализует интерфейс A, то ему также необходим интерфейс B (т. Е. A является специализацией B и, таким образом, существует отношение типа). С другой стороны, ваш результирующий объект должен только записать, что он реализует оба интерфейса, так что там не так много сложностей. Все прекрасно выполнимо.
Отсутствие наследования реализации имеет один очевидный недостаток: вы не сможете создавать vtables с числовыми индексами для ваших классов, и поэтому вам придется выполнять поиск по хеш-функциям для каждого вызова метода (или придумывать хитрый способ их избежать). Это может быть болезненно, если через этот механизм вы направляете даже фундаментальные значения (например, числа). Даже очень хорошая реализация хэша может быть дорогой, если вы нажимаете ее несколько раз в каждом внутреннем цикле!
источник