Действительно ли необходимо наследование?

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

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

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

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

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

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

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

JesperE 10.11.2008 22:04

Позвольте мне дополнить этот комментарий: давайте, создайте свой собственный язык. Вы, вероятно, многому научитесь на этом. Но ПОЖАЛУЙСТА, не публикуйте его! ;-)

dviljoen 10.11.2008 22:17

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

Mendelt 11.11.2008 13:52

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

S.Lott 11.11.2008 14:39

Хорошо, что ни Уолл, ни Мацу, ни ван Россум не подумали «изобретать» новую прогу. язык несколько лет назад был пустой тратой времени. Совсем недавно ни Спольски, ни Грэм, несмотря на большую любовь последнего к LISP, не считали его идеальным.

Joe Pineda 11.11.2008 22:11

@ Джо Пинеда: Чтобы быть конкретным, Пол Грэм очень любит Lisp, но не обязательно Common Lisp. Arc определенно является Лиспом.

David Thornley 05.01.2010 20:03

@ S.Lott: Вы не так много исследуете, верно?

Sebastian Mach 18.12.2014 14:28

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

Matt Thomas 19.09.2018 20:24
В PHP
В PHP
В большой кодовой базе с множеством различных компонентов классы, функции и константы могут иметь одинаковые имена. Это может привести к путанице и...
Принцип подстановки Лискова
Принцип подстановки Лискова
Принцип подстановки Лискова (LSP) - это принцип объектно-ориентированного программирования, который гласит, что объекты суперкласса должны иметь...
34
8
7 343
22
Перейти к ответу Данный вопрос помечен как решенный

Ответы 22

Наследование определяет отношение «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. Есть ссылка от объекта PointWithMassp1 на некоторый объект Point p1-friend. p1-friend имеет атрибуты Point. Когда p1 нужно вести себя как Point, ему нужно делегировать работу своему другу.

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

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

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

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

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

Как быстро ответ! Я понимаю, что ваш первый пункт указывает на то, что наследование stricto senso не требуется - оно просто упростило бы задачу, чтобы PointWithMass вел себя как простой Point там, где это необходимо. Я вас неправильно понял?

Joe Pineda 10.11.2008 20:26

@ Джо Пинеда: Если вы думаете, что этот вопиющий взлом «не нужен», то вы можете принять это так. Я думаю, что вариант 1 - ужасная ошибка, но некоторые люди используют его, чтобы сказать: «Смотрите, наследование необязательно». Они ошибаются в целом, но правы в узком техническом смысле.

S.Lott 10.11.2008 21:29

Ответ быстрый, потому что это стандартный вопрос. Многие люди борются с наследованием и используют всевозможные обходные пути, потому что «is-a» почему-то кажется надуманным или неестественным. Автомобиль - это средство передвижения. Происходит все время. Ответ тоже стандартный.

S.Lott 10.11.2008 21:41

С наследованием я могу использовать PointWithMass в любом месте, где раньше ожидался Point, без каких-либо изменений в этих методах.

Powerlord 10.11.2008 22:14

Отношения типа «is-a» трудно наладить. После прочтения статьи о классе «Set», унаследованном от класса «Bag», причина «набор - это сумка, которая принимает только по одному каждому виду». Затем небольшое изменение (выделение объекта в куче, а не в стеке в конструкторе) в базовом классе привело к созданию тестового приложения с использованием функции Set crash.

Joe Pineda 10.11.2008 22:35

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

S.Lott 10.11.2008 22:44

Я не согласен. МНОГО людей не понимают, почему Set является нет сумкой, а Square - прямоугольником, независимо от того, сколько общих свойств и поведения они имеют. Это неплохая проблема реализации - это сложный на практике принцип Лискова, усложняющий наследование.

Joe Pineda 11.11.2008 00:51

@ Джо Пинеда: Извини. Не по моему опыту. Никогда не было проблем с наследством. Никто из моих сверстников этого не делает. Действительно, проблем с заменой Лискова на практике нет. Итак, я полагаю, что за предыдущее десятилетие OO я не посетил ни одного непонятного края или угловых кейсов. Может, в следующем десятилетии.

S.Lott 11.11.2008 00:56

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

Bazman 11.11.2008 01:36

Обратите внимание, что все языковые функции становятся жертвами «злоупотреблений и злоупотреблений». Кроме того, их все можно обойти, или заменить чем-то лучшим или заменить чем-то худшим.

S.Lott 11.11.2008 06:06

Есть много полезных применений наследования и, вероятно, столько же менее полезных. Один из полезных - класс потока.

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

Но что мешает вам вместо этого реализовать интерфейс IStream? Я думаю, что это было основной темой первоначального вопроса. Например, BufferedStream является ярким примером полезности композиции объекта по сравнению с наследованием.

Jimmy 10.11.2008 20:26
Ответ принят как подходящий

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

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

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

Наследование - это хорошо, когда подкласс действительно является тем же типом объекта, что и суперкласс. Например. если вы реализуете шаблон Active Record, вы пытаетесь сопоставить класс с таблицей в базе данных, а экземпляры класса - с строкой в ​​базе данных. Следовательно, весьма вероятно, что ваши классы Active Record будут иметь общий интерфейс и реализацию таких методов, как: что такое первичный ключ, сохраняется ли текущий экземпляр, сохранение текущего экземпляра, проверка текущего экземпляра, выполнение обратных вызовов при проверке и / или сохранение, удаление текущего экземпляра, выполнение SQL-запроса, возврат имени таблицы, которой соответствует класс, и т. д.

Из того, как вы формулируете свой вопрос, также кажется, что вы предполагаете, что наследование единственное, а не множественное. Если нам нужно множественное наследование, мы должны использовать интерфейсы плюс композицию, чтобы выполнить эту работу. Чтобы уточнить это, Java предполагает, что наследование реализации является единичным, а наследование интерфейсов может быть множественным. Необязательно идти по этому пути. Например. C++ и Ruby допускают множественное наследование для вашей реализации и вашего интерфейса. Тем не менее, следует использовать множественное наследование с осторожностью (т.е. держать абстрактные классы виртуальными и / или без сохранения состояния).

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

В Ruby можно вроде как выполнять множественное наследование, но это скорее единый язык наследования с миксинами. C++ имеет истинное множественное наследование.

Mark Kegel 10.11.2008 20:32

У меня было так много неудач с множественным наследованием в C++, что мне действительно понравилось то, что в Java и C# этого нет. В частности, паттерн «наследование ромба» (B и C наследуются от A, D наследуются от B и c) ОЧЕНЬ сложно реализовать должным образом, с плохой отдачей, на мой взгляд.

Joe Pineda 10.11.2008 20:36

Как я уже сказал, множественное наследование может использовать только действительно опытный человек.

Alan 11.11.2008 09:41

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

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

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

Не нужно, но полезно.

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

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

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

Я считаю, что по крайней мере большинство компиляторов C++ могут оптимизировать функцию-член, которая ничего не делает, кроме вызова члена родительского класса и дословного возврата результата. Согласитесь, это действительно была бы трата времени программистов.

Joe Pineda 10.11.2008 22:50

Однако без наследования не существует родительского класса.

recursive 05.01.2010 19:46

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

Aivar 18.05.2012 16:47

@Aivar Разве это не синтаксический сахар, ключевое слово extends? :) Большинство IDE, с которыми я работаю, имеют какой-то тип вставки кода, где вы можете получить все методы делегата (по крайней мере, общедоступные)

Delfic 14.09.2016 14:50
экспорт в dotty Scala делает это
joel 23.06.2019 02:22

Кто-нибудь еще помнит, как все сторонники объектно-ориентированного подхода взбесились из-за реализации COM «сдерживания» вместо «наследования»? По сути, он достиг того же самого, но с другой реализацией. Это напомнило мне о вашем вопросе.

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

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

Joe Pineda 10.11.2008 21:33

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

Joe Pineda 10.11.2008 21:38

Нет.

для меня ООП - это в основном инкапсуляция состояния, поведения и полиморфизма.

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

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

Зависит от вашего определения «необходимого». Нет, невозможно ничего не может сделать без наследования, хотя альтернатива может потребовать более подробного кода или серьезного переписывания вашего приложения.

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

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

Я считаю, что именно для этих случаев (поведение по умолчанию и переопределение только нескольких методов) композиция лучше. Наследование, как и в книге «Go4», здесь работает как белый ящик, поэтому ваши подклассы зависят от реализации суперкласса.

Joe Pineda 10.11.2008 23:13

Согласитесь с вами в этом: слишком много разработчиков Java и C++ используют очень глубокие иерархии наследования. Из-за этого программное обеспечение очень сложно понять и поддерживать ... Или, возможно, это цель, чтобы убедиться, что у вас будет работа;)

Joe Pineda 10.11.2008 23:16

это делают только действительно дерьмовые, но потом я обнаружил, что эти ребята обращаются к более простым языкам, таким как Java и C#, и начинают критиковать C++ за то, что он "слишком жесткий" :-)

gbjbaanb 11.11.2008 02:40

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

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

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

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

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

Joe Pineda 10.11.2008 23:21

@ Джо, правда ... есть предел любой выгоде. Слишком много хорошего может обернуться плохим.

Kon 11.11.2008 04:20

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

   public abstract class GeneralPresentation
    {
        public GeneralPresentation()
        {
            MenuPages = new List<Page>();
        }
        public IEnumerable<Page> MenuPages { get; set; }
        public string Title { get; set; }
    }

    public class IndexPresentation : GeneralPresentation
    {
        public IndexPresentation() { IndexPage = new Page(); }
        public Page IndexPage { get; set; }
    }

    public class InsertPresentation : GeneralPresentation
    {
        public InsertPresentation() { 
          InsertPage = new Page(); 
          ValidationInfo = new PageValidationInfo(); 
        }
        public PageValidationInfo ValidationInfo { get; set; }
        public Page InsertPage { get; set; }
    }

Я согласен со всеми в отношении необходимого / полезного различия.

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

Достаточно сказать, я люблю ООП.

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

Joe Pineda 10.11.2008 21:48

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

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

Не обязательно, но приятно :)

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

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

Я не мог бы выразить это яснее !!! Многие проблемы, с которыми я столкнулся с наследованием, связаны с тем, что разработчики используют его для «бесплатного» повторного использования, а не для утверждения отношения «есть-а». Языки должны разделять 2 вопроса!

Joe Pineda 10.11.2008 23:06

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

Larry OBrien 10.11.2008 23:49

Обратной стороной композиции является то, что май маскирует взаимосвязь элементов, а май труднее понять другим. С, скажем, классом 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)), которые объединят различные состояния и поведение.

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

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

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

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

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

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

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

ну, проекты «грандиозных схем» «астронавтов архитектуры» всегда оказываются кучей штанов. Проблема не в инструменте, а в рабочих.

gbjbaanb 11.11.2008 02:42

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

Fry 11.11.2008 02:59

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

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

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

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

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

Я предпочитаю, чтобы мое Java-приложение все еще работало после обновления JDK с 1.6 до 1.7 с некоторыми незначительными ошибками (которые могут быть исправлены с течением времени), чем вообще не запускать его (принудительное немедленное исправление, иначе оно будет бесполезно для людей).

// Я нашел этот QA очень полезным. Многие ответили на это правильно. Но я хотел добавить ...

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

2: Наследование помогает типам моделей очень близким к их фактическим отношениям. Иногда во время компиляции обнаруживается множество ошибок, потому что у вас есть правильная иерархия типов. Например, shape <-- triangle (допустим, есть много кода, который нужно использовать повторно). Возможно, вы захотите составить треугольник из объекта shape, но shape - неполный тип. Вставка фиктивных реализаций, таких как double getArea() {return -1;}, подойдет, но вы создаете пространство для ошибок. Этот return -1 когда-нибудь может быть казнен!

3: void func(B* b); ... func(new D()); Неявное преобразование типа дает большое удобство записи, поскольку DerivedявляетсяBase. Я помню, как читал Страуструпа, говорящего, что он хотел сделать классы первоклассными гражданами, такими же, как фундаментальные типы данных (отсюда и операторы перегрузки и т. д.). Неявное преобразование из производного в базовый ведет себя так же, как неявное преобразование из типа данных в более широкий совместимый (сокращенный до int).

У Наследование и Состав есть свои плюсы и минусы.

Обратитесь к соответствующему вопросу SE о плюсах наследования и минусах композиции.

Предпочитаете композицию наследованию?

Взгляните на пример в этом ссылка на документацию:

В этом примере показаны различные варианты использования переопределения с использованием наследования как средства достижения полиморфизма.

Другие вопросы по теме