Я должен использовать наследование?

Это - больше субъективного вопроса, таким образом, я собираюсь заблаговременно отметить его как общественную Wiki.

В основном я нашел, что в большей части моего кода, существует много классов, многие из которых используют друг друга, но немногие из которых непосредственно связаны друг с другом. Я оглядываюсь назад в свои дни колледжа и думаю о традиционном class Cat : Animal введите примеры, где у Вас есть огромные деревья наследования, но я не вижу ни одно из этого в моем коде. Мои диаграммы классов похожи на гигантские паутины, не как хорошие симпатичные деревья.

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

Я могу легко понять разделение на подклассы с точки зрения традиционных примеров такой как class Dog : Animal или class Employee : Person, но у меня просто нет ничего, с чем очевидный я имею дело. И вещи редко так же ясны как class Label : Control. Но когда дело доходит до фактического моделирования реальных объектов в моем коде как иерархия, у меня нет подсказки, где начать.

Так, я предполагаю, что мои вопросы сводятся к этому:

  1. Это в порядке только к не разделяет на подклассы или наследовалось? Я должен быть заинтересован вообще?
  2. Каковы некоторые стратегии, необходимо ли определить объекты, которые могли извлечь выгоду из наследования?
  3. Действительно ли приемлемо всегда наследоваться на основе поведения (интерфейсы), а не фактический тип?
8
задан drharris 22 July 2010 в 18:12
поделиться

11 ответов

Наследование всегда должно представлять отношения "есть - есть". Вы должны иметь возможность сказать "A - это B", если A происходит от B. Если нет, предпочтите композицию. Совершенно нормально не создавать подклассы, если в этом нет необходимости.

Например, говорить, что FileOpenDialog "есть" Window имеет смысл, но говорить, что Engine "есть" Car - бессмыслица. В этом случае экземпляр Engine внутри экземпляра Car является более подходящим (Можно сказать, что Car "is-implemented-in-terms-of" Engine).

Хорошее обсуждение наследования см. в части 1 и части 2 "Uses and Abuses of Inheritance" на gotw.ca.

10
ответ дан 5 December 2019 в 07:10
поделиться
  1. Пока вы не пропустите четкие отношения «есть», все в порядке, и на самом деле лучше не наследовать, а использовать композицию.

  2. is-a - это лакмусовая бумажка. if (Является ли X Y?), то class X: Y {} else class X {Y myY; } или класс Y {X myX; }

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

3
ответ дан 5 December 2019 в 07:10
поделиться

Я также ненавижу примеры собак -> млекопитающих -> животных именно потому, что они не встречаются в реальной жизни.

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

Интерфейсы, однако, великолепны, и вы должны их использовать.

2
ответ дан 5 December 2019 в 07:10
поделиться

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

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

Интересным продолжением этого вопроса могло бы быть: в каких областях программирования действительно хорошо используется наследование? (Фреймворки UI и db уже упоминались и являются отличными примерами. Есть другие?)

3
ответ дан 5 December 2019 в 07:10
поделиться

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

1
ответ дан 5 December 2019 в 07:10
поделиться

IMHO, вам никогда не следует делать №3, если вы не создаете абстрактный базовый класс специально для этой цели, и его имя дает понять, какова его цель:

class DataProviderBase {...}
class SqlDataProvider : DataProviderBase {...}
class DB2DataProvider : DataProviderBase {...}
class AccountDataProvider : SqlDataProvider {...}
class OrderDataProvider : SqlDataProvider {...}
class ShippingDataProvider : DB2DataProvider {...}

и т. Д.

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

Тем не менее, как правило, я использую наследование только в том случае, если у меня есть истинное отношение «есть-а», или если оно улучшит для меня общий дизайн создания «есть-а» отношения (например, модель поставщика)

1
ответ дан 5 December 2019 в 07:10
поделиться

Я бы не стал слишком беспокоиться о том, как выглядит ваша диаграмма классов, вещи редко бывают такими, как в классе ...

Скорее задайте себе два вопроса:

  1. Ваш код работает?

  2. Настало ли время потребляет для обслуживания? Требуется ли изменение иногда одного и того же кода во многих местах?

Если ответ на (2) - да, вы можете посмотреть, как вы структурировали свой код, чтобы увидеть, есть ли более разумный способ, но всегда помня, что в конце дня вы должны быть в состоянии ответить утвердительно на вопрос (1) ... Красивый код, который не работает, никому не нужен, и его сложно объяснить руководству .

0
ответ дан 5 December 2019 в 07:10
поделиться

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

0
ответ дан 5 December 2019 в 07:10
поделиться

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

Например, рассмотрим «преобразователь», используемый для преобразования данных в желаемую форму. Если вы получаете 3 источника данных в виде файлов CSV и хотите поместить их в три разные объектные модели (и, возможно, сохранить их в базе данных), вы можете создать базу «преобразователь csv», а затем переопределить некоторые методы при наследовании от нее в чтобы обрабатывать различные конкретные объекты.

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

Кроме того, если вы держите свои слои отдельно (бизнес, данные, представление и т. Д.), Ваша диаграмма классов будет проще, и вы сможете «визуализировать» те объекты, которые должны быть унаследованы.

0
ответ дан 5 December 2019 в 07:10
поделиться

Как правило, композиция предпочтительнее наследования. Наследование имеет тенденцию нарушать инкапсуляцию . например Если класс зависит от метода суперкласса и суперкласс изменяет реализацию этого метода в некоторой версии, подкласс может сломаться.

Иногда, когда вы проектируете фреймворк, у вас будет для проектирования классов, которые будут унаследованы. Если вы хотите использовать наследование, вам придется тщательно задокументировать и спроектировать его. например Не вызывать никаких методов экземпляра (которые могут быть переопределены вашими подклассами) в конструкторе. Кроме того, если это настоящая связь «есть-а», наследование полезно, но более надежно, если оно используется в пакете.

См. Эффективная Java (пункты 14 и 15). Это отличный аргумент в пользу того, почему вы должны отдавать предпочтение композиции, а не наследованию. Он говорит о наследовании и инкапсуляции в целом (с примерами java). Так что это хороший ресурс, даже если вы не используете java.

Итак, чтобы ответить на ваши 3 вопроса:

Можно ли просто не создавать подклассы или наследовать? Стоит ли мне вообще беспокоиться? Ответ: Задайте себе вопрос, действительно ли это настоящие отношения? Возможно ли украшение? Пойдите для украшения

// A collection decorator that is-a collection with 
public class MyCustomCollection implements java.util.Collection {
    private Collection delegate;
    // decorate methods with custom code
}

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

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

2
ответ дан 5 December 2019 в 07:10
поделиться

Если у вас общие функции, программирование на интерфейс более важно, чем наследование.

По сути, наследование больше связано с объединением объектов.

Большую часть времени нас интересует, что объект может ДЕЛАТЬ, а не то, что он собой представляет.

class Product
class Article
class NewsItem

Являются ли NewsItem и Статья Контентом ? Возможно, и вам может быть полезно иметь список контента, который содержит как элементы Article , так и элементы NewsItem .

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

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

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

1
ответ дан 5 December 2019 в 07:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: