Коли використовувати спадщину, коли використовувати "просто булеве поле"?


18

У нашому додатку Rails ми додаємо сповіщення. Деякі з них такі blocking: вони зупиняють хід виконання будь-якого ресурсу, до якого вони додаються, оскільки деякої інформації про цей ресурс бракує.

Інші сповіщення - це прості сповіщення та надають лише інформацію.

Сьогодні я мав дискусію з іншим програмістом нашої команди. Я створив структуру спадкування так:

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

Він, однак, хотів би, щоб я просто додав blockingметод булевого повернення у кожне Повідомлення та вказав список підкласів, що блокують всередині батьківського класу Повідомлення.

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

Яка абстракція більше підходить для цієї проблеми?


11
Батьківський клас ніколи не повинен знати про це діти. Чому потрібно вести список підкласів?
coteyr

Як вони додаються на ресурс і як зупиняють його прогрес?
недійсне

3
Навіщо вам потрібно стільки класів сповіщень? Мені здається, ви можете створити один клас сповіщень, а потім дозволити даним керувати діями замість типів даних.
Trisped

1
@Trisped: правильно, якщо ви виявите, що ви переходите один аспект поведінки до базового класу із вичерпним списком випадків, то майте сміливість своїх переконань, визнайте, що ви насправді не проектуєте корисний базовий клас для налаштування за допомогою розширення , і перенесіть всю поведінку в базовий клас!
Стів Джессоп

Відповіді:


35

Ви хочете уникати базових класів, знаючи про похідні класи. Він вводить тісний зв'язок і є головним болем у технічному обслуговуванні, оскільки вам потрібно пам’ятати, що потрібно додавати до списку щоразу, коли ви створюєте новий похідний клас.

Це також не дозволить вам помістити клас сповіщень у пакет / збірку для багаторазового використання, якщо ви хочете використовувати цей клас у кількох проектах.

Якщо ви дійсно хочете використовувати один базовий клас, інший спосіб вирішити це - додати віртуальну властивість або метод IsBlocking до базового класу повідомлень. Отримані класи можуть потім перекрити це, щоб повернути істинне або хибне. У вас було б рішення одного класу, не знаючи базового класу про похідні класи.


3
Це. Приймайте рішення в одному місці. Не розкидайте знання про те, які класи блокуються між класом і списком.
candied_orange

Це те, що я роблю, працює дуже добре і дуже багаторазово використовувати.
coteyr

13

і вказати список підкласів, які блокуються всередині батьківського класу сповіщень.

Це виглядає дуже своєрідно і є особливим кодовим запахом.

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


1
Я думаю, що "поведінка" є ключовим тут: коли це лише дані, поле повинно бути достатнім дискримінатором. Поведінка є кращою причиною використання поліморфізму, проте завжди слід враховувати складність обслуговування при створенні спадкових ієрархій. Читати en.wikipedia.org/wiki/Composition_over_inheritance
cottsak

7

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

Однак, кращим дизайном навіть у цій ситуації може бути використання об'єкта «Декоратор».


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

Замість створення двох базових класів і декількох примірників кожного, зробіть один клас сповіщень з bool, щоб вказати, чи блокується сповіщення та будь-яку іншу інформацію, необхідну для повідомлення користувача користувачу.

Це дозволяє використовувати один набір коду для обробки та подання сповіщень та зменшує складність вашого коду.

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