Когда использовать наследование, когда использовать «просто логическое поле»?

18

В нашем приложении Rails мы добавляем уведомления. Вот некоторые из них blocking: Они останавливают прогресс любого ресурса, к которому они добавлены, потому что некоторая информация об этом ресурсе отсутствует.

Другие уведомления являются простыми уведомлениями и предоставляют только информацию.

Сегодня у меня была дискуссия с другим программистом в нашей команде. Я создал структуру наследования следующим образом:

введите описание изображения здесь

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

Разница между этими подходами не очень большая; в моем подходе нет необходимости указывать этот список, сохраняя корневой класс чище. С другой стороны, особая логика, которая происходит Notification::Blockingсейчас, тоже не очень велика.

Какая абстракция больше подходит для этой проблемы?

Qqwy
источник
11
Родительский класс никогда не должен знать о своих детях. Зачем вам нужен список подклассов?
Котейр
Как они добавляются в ресурс и как они останавливают его продвижение?
ноль
3
Зачем вам нужно так много классов уведомлений? Похоже, вы могли бы создать один класс уведомлений, а затем позволить данным управлять действиями вместо типов данных.
Trisped
1
@Trisped: верно, если вы обнаружите, что переносите один аспект поведения в базовый класс с исчерпывающим списком дел, то наберитесь смелости своих убеждений, признайте, что вы на самом деле не разрабатываете используемый базовый класс для настройки путем расширения и перенести все поведение в базовый класс!
Стив Джессоп

Ответы:

35

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

Это также не позволит вам поместить класс Notification в повторно используемый пакет / сборку, если вы хотите использовать этот класс в нескольких проектах.

Если вы действительно хотите использовать один базовый класс, другой способ решить эту проблему - добавить виртуальное свойство или метод IsBlocking в базовый класс Notification. Производные классы могут затем переопределить это, чтобы вернуть истину или ложь. У вас будет единственное решение для класса без знания базового класса о производных классах.

17 из 26
источник
3
Эта. Принимайте решения в одном месте. Не разбрасывайте знания о том, какие классы блокируют между классом и списком.
candied_orange
Это тот, который я делаю, работает очень хорошо и очень многоразового использования.
Котейр
13

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

Это выглядит очень странно и является специфическим запахом кода.

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

Брайан Агнью
источник
1
Я думаю, что «поведение» является ключом здесь: когда это просто данные, поле должно быть достаточным дискриминатором. Поведение - лучшая причина для использования полиморфизма, однако всегда следует учитывать сложность обслуживания при создании иерархий наследования. Читайте en.wikipedia.org/wiki/Composition_over_inheritance
коттсак
7

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

Тем не менее, лучшим дизайном даже в этой ситуации может быть использование объекта Decorator.

Жюль
источник
1

Я бы сказал, что это зависит от того, что еще особенного в уведомлении о блокировке, хотя моя первая мысль - использовать «оба»:

class Notification
 virtual Boolean Blocking{get return false;}

class BlockingNotification inherits Notification
 virtual overrides Boolean Blocking{get return true;}

Таким образом, вы можете использовать n.Blockingили n is BlockingNotification(все в псевдокоде), хотя, если вы собираетесь позволить классу реализовать контекстно-зависимое Blockingзначение, видя, как вам придется проверять это значение каждый раз, BlockingNotificationкласс становится менее полезен

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

Марк Херд
источник
0

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

Это позволяет вам использовать один набор кода для обработки и представления уведомлений и уменьшает сложность вашего кода.

Trisped
источник