Спадщина пішла не так


12

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

class Animal  
class Parrot : Animal 
class Elephant : Animal 
class Cow : Animal

тощо.

У вас є ваші методи харчування (), run () тощо, і все добре. Потім одного дня хтось підійде і каже - наш клас CageBuilder чудово працює і використовує animal.weight () і animal.height (), за винятком нового африканського зубра, який занадто сильний і може розбити стіну, тому я хочу додати ще одна властивість класу Animal - цеAfricanBizon (), і використовуйте його, вибираючи матеріал і переосмислюйте його лише для класу AfricanBizon. Наступна людина приходить і робить щось подібне, і наступне, що ви знаєте, у вас є всі ці властивості, характерні для деякого підмножини ієрархії в базовий клас.

Який хороший спосіб поліпшити / рефактор такого коду? Однією з альтернатив тут може бути просто використання динамічних передач, щоб перевірити типи, але це захаращує абонентів і додає купу if-then-else всюди. Тут ви можете мати більш конкретні інтерфейси, але якщо все, що вам потрібно, - це базовий клас, який теж не допомагає. Будь-які інші пропозиції? Приклади?

Дякую!


@James: тоді вам доведеться писати парсери вручну. : S
Matteo Italia

5
Зрозуміло, що це випадок смішного вимоги замовника. В Африці бізонів немає. Ви не можете створювати об'єктні моделі, не пов'язані з реальністю. Якщо тільки реальність не створена руками, повними доларів. Що вирішує проблему.
Ганс Пасант

1
З'їсти всіх зубрів? [Я публікував це раніше, але його чомусь видалили, імовірно, безглузді шакани.]
Джеймс Мак-Нілліс

Чи потрібен CageBuilder власний клас? Що робити, якщо існує метод MakeCage за замовчуванням, який може бути замінено кожним окремим класом.
робота

1
Ви згадуєте, що хаос у тому випадку, як недолік для абонентів, але, як тільки користувачі починають використовувати isAfricanBizon (), вони забивають код автоматично if-then-else. Тож це або переповнення if-then-else з isAfricanBizon (), або незграбність if-then-else з динамічними кастами.
davidk01

Відповіді:


13

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


1
-1: Тільки каже, що не робити. В ОП вже знають, що це була погана ідея, звідси і питання.
Стівен Еверс

12

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

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


+1: Схоже, всі інші порушують концептуальну модель класу (яка просто інкапсулює властивості різних видів тварин), щоб відповідати цьому конкретному використанню. strengthМетод може бути запитаний material.canHold(animal), дозволяючи чистий спосіб підтримки різних видів матеріалу , ніж ConcreteWall.
Айдан Каллі

Мені подобається підхід властивості force () краще, ніж пропозиції інших щодо RequiresConcreteWall (), тому що він більш гнучкий для забезпечення майбутніх вимог. Для початку змусьте клас CageBuilder вирішити, які матеріали досить міцні, і тоді ви можете легко поширити клас новими матеріалами.
ривок

3

Я думаю, що ваша проблема полягає в цьому: у вас є різні клієнти бібліотеки, яких цікавить лише підмножина ієрархії, але їм передається покажчик / посилання на базовий клас. Це насправді питання, яке динамічний_кас <> є для вирішення.

Справа в розробці клієнтів, щоб мінімізувати використання динамічних передач <>; вони повинні використовувати його, щоб визначити, чи потрібен об'єкт спеціального поводження, і якщо це зробити, всі операції за посиланням вниз.

Якщо у вас є набори функцій "змішаного" типу, що застосовуються до декількох окремих підієрархій, можливо, ви захочете використовувати шаблон інтерфейсу, який використовують Java та C #; мати віртуальний базовий клас, який є чисто-віртуальним класом, і використовуйте динамический_кас <> для визначення, чи примірник забезпечує реалізацію для нього.


1

Одне, що ви можете зробити, це замінити явну перевірку типу типу isAfricanBison()на перевірку властивостей, які вас насправді цікавлять, тобто isTooStrong().


1
isTooStrong () для чого? Ви додаєте специфічний код у клітці до класу тварин.
Стівен Еверс

1

Тваринам не варто піклуватися про бетонні стіни. Можливо, ви можете висловити це простими значеннями.

class Animal {
public:
  virtual ~Animal() {}
  virtual size_t height() const = 0;
  virtual size_t weight() const = 0;
  virtual bool isStrong() const = 0;
};

Cage *CreateCageFromSQL(Animal &a);
Cage *CreateCageFromOrangePeelsAndSticks(Animal &a);

Я підозрюю, що це не життєздатно. В цьому і полягає проблема з іграшковими прикладами.

Я ніколи не хотів би бачити в будь-якому випадку RequiresConcreteWalls () або лінії та лінії динамічних відкидних покажчиків.

Зазвичай це дешеве рішення. Її легко підтримувати та концептуалізувати. І справді, проблема стверджує, що його так чи інакше прив’язують до типу тварини.

class Animal {
public:
  virtual ~Animal() {}
  virtual CageBuilder *getCageBuilder() = 0;
};

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

Але те, як будується клітка, може бути політикою якоїсь іншої системи, і, можливо, у вас є більше одного типу будівельника клітки на одну тварину. Існує багато дивних і заплутаних комбінацій, які ви можете придумати.

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

Подвійна відправка - це ще один варіант, хоча я завжди був стриманий, щоб стрибнути в неї.

Крім того, важко здогадатися про проблему.


0

Звичайно, всі тварини мають властивість attemptEscape(). Хоча деякі методи можуть спричинити falseрезультат у всіх сценаріях, тоді як інші можуть мати шанс виходячи з евристики інших їхніх внутрішніх характеристик, таких як sizeі weight. Тоді, безумовно, в якийсь момент attemptEscape()стає банальним, оскільки це, безумовно, повернеться true.

Боюся, я не повністю розумію ваше питання, хоча ... всі тварини мають суміжні дії та характеристики. Особи, характерні для тварини, слід вводити там, де вона підходить. Намагання безпосередньо пов’язати Бізона з Папугами - це не дуже вдале налаштування нечесності, і це не повинно бути проблемою у належному дизайні.


-1

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


-1

як щодо рекомендаціїCareType (), як увімкнено для RequiresConcreteWall ()


-2

Чому б не зробити щось подібне

class Animals { /***/ } class HeavyAnimals{} : Animals //The basic class for animals like the African Bison

За допомогою класу HeavyAnimals ви можете створити клас African Bison, розширивши клас HeavyAnimals.

Тепер ви, батьківський клас (тварини), який можна використовувати для створення іншого базового класу, наприклад клас HeavyAnimal, можна використовувати для створення класу африканських зубрів та інших важких тварин. Тож із африканським Бізоном ви тепер маєте доступ до методів та властивості класу Animal (це база для всіх тварин) та доступ до класу HeavyAnimals (це база для важких тварин)


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