Действительно ли наследование необходимо? - PullRequest
32 голосов
/ 10 ноября 2008

Должен признаться, я отчасти скептик ООП. Плохой педагогический и лабораторный опыт с объектной ориентацией не помог. Таким образом, я превратился в горячего сторонника Visual Basic (классического!).

Затем однажды я узнал, что C ++ изменился и теперь имеет STL и шаблоны. Мне действительно понравилось это! Сделал язык полезным. Затем в другой день MS решила применить лицевую хирургию к VB, и я действительно ненавидела конечный результат за необоснованные изменения (использование «end while» вместо «wend» сделает меня лучшим разработчиком? Почему бы не бросить «next» для » конец для "тоже? Зачем заставлять геттер вместе с сеттером? И т. д.) плюс так много функций Java, которые я нахожу бесполезными (например, наследование и концепция иерархической структуры).

И теперь, спустя несколько лет, я задаю себе философский вопрос: действительно ли необходимо наследование ?

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

Есть идеи? Я хотел бы видеть пример, где наследование определенно будет необходимо, или где использование наследования вместо интерфейсов композиции + может привести к более простому и легкому изменению дизайна. В предыдущих заданиях, которые я обнаружил, если вам нужно изменить базовый класс, вам нужно также изменить почти все производные классы, поскольку они зависят от поведения родительского класса. И если вы сделаете методы базового класса виртуальными ... тогда не будет большого совместного использования кода: (

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

Ответы [ 22 ]

33 голосов
/ 10 ноября 2008

Действительно очень короткий ответ: Нет. Наследование не нужно, потому что действительно нужен только байт-код. Но очевидно, что байт-код или ассемблер не являются практически способом написания вашей программы. ООП - не единственная парадигма программирования. Но я отвлекся.

Я поступил в колледж компьютерных наук в начале 2000-х годов, когда наследование (is a), композиции (has a) и интерфейсы (has a) преподавались на равных. Из-за этого я использую очень мало наследования, потому что оно часто лучше подходит для композиции. Это было подчеркнуто, потому что многие профессора видели плохой код (наряду с тем, что вы описали) из-за злоупотребления наследованием.

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

22 голосов
/ 10 ноября 2008

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

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

14 голосов
/ 10 ноября 2008

Наследование определяет отношение "Is-A".

class Point( object ):
    # some set of features: attributes, methods, etc.

class PointWithMass( Point ):
    # An additional feature: mass.

Выше я использовал наследование, чтобы формально объявить, что PointWithMass - это Point.

Существует несколько способов обработки объекта P1, являющегося как PointWithMass, так и Point. Вот два.

  1. Имеют ссылку от PointWithMass объекта p1 на некоторый объект Point p1-friend. p1-friend имеет атрибуты Point. Когда p1 необходимо участвовать в Point -подобном поведении, ему необходимо делегировать работу своему другу.

  2. Положитесь на наследование языка, чтобы гарантировать, что все функции Point также применимы к моему PointWithMass объекту p1. Когда p1 нужно участвовать в Point -подобном поведении, он уже является Point объектом и может просто делать то, что нужно сделать.

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

Редактировать.

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

Чтобы узнать о неясном злоупотреблении наследованием, прочитайте о странном болоте «сочинения через частное наследование» в C ++. См. Какие-нибудь разумные примеры создания наследования без создания отношений подтипов? для дальнейшего обсуждения этого вопроса. Это объединяет наследование и состав; кажется, что это не добавляет ясности или точности к результирующему коду; это относится только к C ++.

12 голосов
/ 10 ноября 2008

GoF (и многие другие) рекомендуют, чтобы вы только отдавали предпочтение композиции над наследованием. Если у вас есть класс с очень большим API, и вы хотите добавить к нему очень небольшое количество методов, оставив базовую реализацию в одиночку, я считаю, что использование композиции неуместно. Вам нужно будет повторно реализовать все открытые методы инкапсулированного класса, чтобы просто вернуть их значение. Это пустая трата времени (программист и процессор), когда вы можете просто унаследовать все это поведение и посвятить свое время новым методам.

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

11 голосов
/ 10 ноября 2008

Проблема с наследованием заключается в том, что он объединяет проблему подтипа (утверждение отношения is-a) и повторного использования кода (например, частное наследование предназначено только для повторного использования).

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

7 голосов
/ 10 ноября 2008

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

3 голосов
/ 10 ноября 2008

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

Все, что заботится о бизнесе, производит (качество конечно) дешево и быстро .

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

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

3 голосов
/ 11 ноября 2008

Наследование является решением о реализации. Интерфейсы почти всегда представляют собой лучший дизайн и обычно должны использоваться во внешнем API.

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

Этот ответ на другой вопрос довольно хорошо обобщает мое мышление.

3 голосов
/ 10 ноября 2008

Недостатки композиции в том, что она может скрывать взаимосвязь элементов, а может труднее понять другим. С, скажем, классом 2D Point и желанием расширить его до более высоких измерений, вам, вероятно, придется добавить (по крайней мере) Z getter / setter, изменить getDistance () и, возможно, добавить метод getVolume (). Итак, у вас есть элементы Objects 101: связанное состояние и поведение.

Разработчик с композиционным мышлением предположительно определил бы метод getDistance (x, y) -> double и теперь определил бы метод getDistance (x, y, z) -> double. Или, вообще говоря, они могут определить метод getDistance (lambdaGeneratingACoordinateForEveryAxis ()) -> double. Тогда они, вероятно, напишут методы фабрики createTwoDimensionalPoint () и createThreeDimensionalPoint () (или, возможно, createNDimensionalPoint (n)), которые будут объединять различные состояния и поведение.

Разработчик с OO мышлением будет использовать наследование. Та же сложность в реализации характеристик домена, меньшая сложность с точки зрения инициализации объекта (конструктор заботится о нем по сравнению с методом Factory), но не такая гибкая с точки зрения того, что может быть инициализированным.

Теперь подумайте об этом с точки зрения понятности / читабельности. Чтобы понять состав, имеется большое количество функций, которые программно составлены внутри другой функции. Так что с точки зрения статической структуры кода (файлов, ключевых слов и т. Д.) Мало что делает взаимосвязь Z и distance () выпрыгивающей . В ОО-мире у вас есть огромный мигающий красный свет, говорящий вам об иерархии. Кроме того, у вас есть практически универсальный словарь для обсуждения структуры, широко известных графических обозначений, естественной иерархии (по крайней мере, для одиночного наследования) и т. Д.

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

В профессиональной среде с опытными разработчиками гибкость композиции обычно превосходит ее более абстрактную природу. Однако никогда не следует сбрасывать со счетов важность понимания, особенно в командах, которые имеют разную степень опыта и / или высокий уровень текучести кадров.

3 голосов
/ 10 ноября 2008

Кто-нибудь еще помнит, как все ОО-пуристы баллистически относились к реализации СОМ «сдерживания» вместо «наследования»? По сути, это то же самое, но с другим типом реализации. Это напоминает мне ваш вопрос.

Я строго стараюсь избегать религиозных войн в разработке программного обеспечения. ("vi" ИЛИ "emacs" ... когда каждый знает его "vi"!) Я думаю, что они являются признаком маленьких умов. Профессора Comp Sci могут позволить себе сидеть и обсуждать эти вещи. Я работаю в реальном мире и мне наплевать. Все это просто попытки дать полезные решения реальных проблем. Если они работают, люди будут их использовать. Тот факт, что ОО-языки и инструменты широко доступны на коммерческой основе в течение 20 лет, - это неплохая ставка на то, что они полезны для многих людей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...