Должен ли класс знать о своих подклассах?

24

Должен ли класс знать о своих подклассах? Должен ли класс делать что-то специфичное для данного подкласса, например?

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

m4design
источник
6
Обычно нет, но в особых случаях это полезно.
CodesInChaos
Это является анти-модель, хорошо известная один называется: «Лисков принцип замещения» или иногда просто LSP, читайте об этом в en.wikipedia.org/wiki/Liskov_substitution_principle
Джимми Хоффа
5
@JimmyHoffa - «Принцип замещения Лискова» - это не название анти-паттерна. Анти-паттерн может нарушать LSP, но даже не уверен, что это будет правдой здесь. Возможно, что, даже если тип знает, что это подтипы, эти подтипы могут быть заменены их родителями на контрастную и ковариантную.
Мэтью Флинн
4
@MatthewFlynn Возможно, я использую LSP немного свободно, но по моему определению, это не факт, что все они соответствуют требованиям друг друга, которые соответствуют LSP, это требование, чтобы они имели разделение, так что они не только соглашались друг с другом, но и Вы могли бы реализовать другой подкласс, который не нарушает LSP. Если иерархия самоосознает, то внешняя реализация не сможет удовлетворить LSP без изменения базового класса. Возможно, это не совсем LSP, но это очень близко. и да, я должен был сказать, что нарушение LSP является анти-паттерном
Джимми Хоффа
2
Несколько раз я сталкивался с этим, я преобразовывал эти знания в метод, который можно переопределить в подклассе (либо принудительно, сделав его абстрактным, либо чисто виртуальным, либо предоставив разумную реализацию по умолчанию, которую можно переопределить).

Ответы:

32

Ответ, подразумеваемый понятием классов, - «нет».

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

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

Килиан Фот
источник
4
Единственное исключение из этого правила: в документации класса должны быть перечислены его подклассы, локальные для его собственной библиотеки.
Кьевели
3
@Kieveli ... до тех пор, пока эта документация обновляется.
Mouviciel
14
Любой полезный инструмент документирования должен уметь рисовать деревья наследования ...
Йоханнес
13
Я не думаю, что это исключение. Класс до сих пор ничего не знает. Документация знает.
Хонза Брабек
4
@Kieveli эээ, я полностью не согласен.
Джимми Хоффа
16

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

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

В Scala это используется для моделирования замкнутых алгебраических типов данных, поэтому канонический Listтип Haskell :

data List a = Nil | Cons a (List a)

можно моделировать в Scala следующим образом:

sealed trait List[+A]

case object Nil extends List[Nothing]

final case class Cons[+A] extends List[A]

И вы можете гарантировать, что существуют только эти два подкласса, потому что он Listесть sealedи, следовательно, не может быть расширен за пределы файла, Consесть finalи, следовательно, не может быть расширен вообще, и Nilон не objectможет быть расширен в любом случае.

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

Йорг Миттаг
источник
Что работает на многих языках, так это добавление некоторой формы abstract internalчлена.
CodesInChaos
4

Простой ответ - нет.

Это делает код хрупким и нарушает два основных принципа объектно-ориентированного программирования.

  • Принцип подстановки Лискова
  • Принцип Open Close
Саджад Деяргару
источник
1

Иногда да. Например, когда существует ограниченное количество подклассов, существуют. Паттерн посетитель является иллюстрацией полезности этого подхода.

Пример: узлы абстрактного синтаксического дерева (AST) некоторой четко определенной грамматики могут быть все унаследованы от одного Nodeкласса, реализующего шаблон посетителя для обработки всех типов узлов.

Lorus
источник
9
Нет, нет и нет. Это не полезно и никогда не является хорошим решением.
Султан
@Sulthan, я работал с AST вне классной комнаты, и я полагаю, что Лорус действительно не понимает вопрос. Посетитель - это не тип узла в AST, это отдельный объект. Он может и должен знать об объектах грамматики. Но высокий уровень узла AST не должен.
1
@JohnGaughan Термин «знает» меня смущает. Базовый Nodeкласс, конечно, не содержит прямых ссылок на свои подклассы. Но он содержит acceptметод с Visitorпараметром. И Visitorсодержит visitметод для каждого подкласса. Таким образом, несмотря на то, что Nodeон не имеет прямых ссылок на свои подклассы, он «знает» о них косвенно, через Visitorинтерфейс. Они все связаны вместе через это.
Lorus
1
@Sulthan Никогда не говори никогда. Опция type является допустимым примером случая, когда верхний класс знает о потомке. Но, как сказал Йорг, это будет помечено как запечатанное.
Павел Воронин
Тогда соглашаетесь: посетитель должен знать, что он посещает.
1

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

Нет!

То же самое с классами. Доверяй своим инстинктам.

Даниэль Холлинрейк
источник
Безопасность работы ???
sixtyfootersdude
Ваш рост шестьдесят футов, так что я не буду с вами спорить :-) Однако я надеюсь, что мой смысл был после того, как я ушел и получил другую работу.
Даниэль Холлинрейк