Чи повинен клас знати про його підкласи?


24

Чи повинен клас знати про його підкласи? Чи повинен клас робити щось, що є специфічним для даного підкласу, наприклад?

Мої інстинкти говорять мені, що це погана конструкція, здається, якась антидіаграма.


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
Будь-який застосований інструмент документації повинен мати можливість малювати спадкові дерева ...
johannes

13
Я не думаю, що це виняток. Клас досі нічого не знає. Документація знає.
Honza Brabec

4
@Kieveli uhh Я повністю не згоден.
Джиммі Хоффа

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

Проста відповідь - Ні.

Це робить код крихким і змінює два основні принципи об'єктно-орієнтованого програмування.

  • Принцип заміщення Ліскова
  • Принцип відкриття закриття

1

Так, інколи. Наприклад, коли існує обмежена кількість підкласів. шаблон відвідувача є ілюстрацією корисності цього підходу.

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


9
Ні, ні і ні. Це не корисне і ніколи хороше рішення.
Султан

@Sulthan Я працював з AST за межами аудиторії, і я вважаю, що Лорус не дуже розуміє питання. Відвідувач - це не тип вузла AST, це окремий об'єкт. Він може і повинен знати про граматичні предмети. Але вузол високого рівня AST не повинен.

1
@JohnGaughan Термін "знає" мене бентежить. Базовий Nodeклас, звичайно, не містить прямих посилань на свої підкласи. Але він містить acceptметод з Visitorпараметром. І Visitorмістить visitметод для кожного підкласу. Отже, незважаючи на те, що не Nodeмає прямих посилань на свої підкласи, він "знає" про них опосередковано, через Visitorінтерфейс. Всі вони з'єдналися через це.
lorus

1
@Sulthan Ніколи не кажи ніколи. Тип варіанту є вагомим прикладом випадку, коли надклас знає про нащадковий. Але, як сказав Йорг, це буде позначено як запечатане.
Павло Воронін

Тоді домовляємось: відвідувач повинен знати, що він відвідує.

1

Якщо я напишу компонент для фірми, а пізніше, після того як я пішов, хтось продовжує його для власних цілей, чи повинен я бути проінформований про це?

Ні!

Те саме з заняттями. Довіряйте своїм інстинктам.


Безпека роботи ???
шістдесят футів

Ваш шістдесят футів у висоту, тому я не можу з вами сперечатися :-) Однак я сподіваюся, що мій сенс був після того, як я пішов і влаштувався на іншу роботу.
Даніель Холлінрак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.