Должен ли класс знать о своих подклассах? Должен ли класс делать что-то специфичное для данного подкласса, например?
Мои инстинкты говорят мне, что это плохой дизайн, это похоже на какой-то анти-паттерн.
Должен ли класс знать о своих подклассах? Должен ли класс делать что-то специфичное для данного подкласса, например?
Мои инстинкты говорят мне, что это плохой дизайн, это похоже на какой-то анти-паттерн.
Ответы:
Ответ, подразумеваемый понятием классов, - «нет».
Независимо от того, какое действие, данные или отношение вы обрабатываете, является частью всех подклассов - тогда они должны обрабатываться в суперклассе без проверки фактического типа. Или это применимо только к некоторым подклассам - тогда вам придется выполнять проверки типов во время выполнения, чтобы сделать правильные вещи, суперкласс должен быть изменен всякий раз, когда кто-то другой наследует его (или он может молча делать неправильные вещи), изменения в производных классах могут сломать неизменный суперкласс и т. д.
Короче говоря, вы получаете ряд плохих последствий, которые, как правило, достаточно плохи, чтобы отклонить такие решения из-под контроля. Если несколько ваших подклассов делают одно и то же, и вы хотите избежать дублирования кода (практически всегда это хорошо), лучшим решением будет введение класса среднего уровня, от которого все эти подклассы могут наследовать код.
Мало того, что он не знает, он просто не может ! Обычно класс может быть расширен в любое время и в любом месте. Это может быть расширено классами, которые даже не существовали, когда это было написано.
Некоторые языки позволяют расширять классы, контролируемые суперклассом. В 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
члена.
Простой ответ - нет.
Это делает код хрупким и нарушает два основных принципа объектно-ориентированного программирования.
Иногда да. Например, когда существует ограниченное количество подклассов, существуют. Паттерн посетитель является иллюстрацией полезности этого подхода.
Пример: узлы абстрактного синтаксического дерева (AST) некоторой четко определенной грамматики могут быть все унаследованы от одного Node
класса, реализующего шаблон посетителя для обработки всех типов узлов.
Node
класс, конечно, не содержит прямых ссылок на свои подклассы. Но он содержит accept
метод с Visitor
параметром. И Visitor
содержит visit
метод для каждого подкласса. Таким образом, несмотря на то, что Node
он не имеет прямых ссылок на свои подклассы, он «знает» о них косвенно, через Visitor
интерфейс. Они все связаны вместе через это.
Если я напишу компонент для фирмы и позже, после того как я уйду, кто-то расширит его для своих собственных целей, должен ли я быть проинформирован об этом?
Нет!
То же самое с классами. Доверяй своим инстинктам.