Об'єктно-орієнтований дизайн


23

Припустимо, у вас є наступне:

     +--------+     +------+
     | Animal |     | Food |
     +-+------+     +----+-+
       ^                 ^
       |                 |
       |                 |
  +------+              +-------+
  | Deer |              | Grass |
  +------+              +-------+

Deerуспадковує від Animal, а Grassуспадковує від Food.

Все йде нормально. Animalпредмети можуть їсти Foodпредмети.

Тепер давайте трохи змішати його. Додамо додавання, Lionяке успадковується від Animal.

     +--------+     +------+
     | Animal |     | Food |
     +-+-----++     +----+-+
       ^     ^           ^
       |     |           |
       |     |           |
  +------+ +------+     +-------+
  | Deer | | Lion |     | Grass |
  +------+ +------+     +-------+

Зараз у нас є проблема, тому що Lionможна їсти і те, Deerі це Grass, але Deerце не Foodтак Animal.

Як ви вирішите цю проблему, використовуючи багаторазове успадкування та використовуючи об'єктно-орієнтований дизайн?

FYI: Я використовував http://www.asciiflow.com для створення діаграм ASCII.


14
Моделювання реального світу, як правило, рано чи пізно є проблемою, тому що завжди відбувається щось дивне (наприклад, літаюча риба, риба чи птах? Але пінгвін - птах, не вміє літати і їсть рибу). Те, що говорить @Ampt, звучить правдоподібно, тварина повинна мати колекцію речей, які вона їсть.
Роб ван дер Веер

2
Я думаю, що Тварина повинна успадковувати від Їжі. Якщо щось намагається з'їсти Лева, просто киньте InvalidOperationException.
RalphChapin

4
@RalphChapin: Лев їсть усі види речей (грифи, клопи тощо). Я думаю, що тварини та їжа - це штучні розрізнення, які будуть руйнуватися, оскільки вони недостатньо широкі (зрештою, всі тварини є їжею інших тварин). Якщо ви клацаєте на "LivingThing", вам доведеться мати справу лише з крайовими справами з рослинами, які їдять неживі речі (мінерали тощо), і нічого не порушить, щоб мати LivingThing.Eat (LivingThing).
Satanicpuppy

2
Обмін дослідженнями допомагає всім. Розкажіть, що ви пробували і чому це не відповідало вашим потребам. Це свідчить про те, що ви знайшли час, щоб спробувати допомогти собі, це позбавляє нас від повторення очевидних відповідей, а найбільше це допомагає вам отримати більш конкретну та релевантну відповідь. Також дивіться Як просити
gnat

9
На це питання відповіла гра Age of Empire III. ageofempires.wikia.com/wiki/List_of_Animals Олень та Газель реалізують IHuntable, Вівці та Корова IHerdable(керовані людиною), а Лев реалізує лише IAnimal, що не передбачає жодного з цих інтерфейсів. AOE3 підтримує запит набору інтерфейсів, підтримуваних певним об'єктом (подібним до instanceof), що дозволяє програмі запитувати його можливості.
rwong

Відповіді:


38

IS A відносини = Спадщина

Лев - тварина

МАЄ стосунки = Склад

У автомобіля є колесо

МОЖУТЬ робити стосунки = інтерфейси

ICanEat


5
+1 Це так просто, але все ж такий хороший підсумок трьох різних типів відносин
dreza

4
Альтернатива: ICanBeEatenабоIEdible
Майк Веллер

2
МОЖЕ ВЗАЄМО стосунки = lolcats
Стівен А. Лоу

1
Як це відповідає на питання?
користувач253751

13

ОО - це лише метафора, яка моделює себе після реального світу. Але метафори йдуть лише так далеко.

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

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

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


3
+1 OO - це інструмент, а не релігія.
mouviciel

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

Ви серйозно думаєте, що реальний світ моделюється в ОП? Модель відносин представлена ​​на прикладі.
Василевс

@Basilevs Це наслідок насправді, оскільки він згадує, як поводяться тварини в реальному житті. Потрібно перейматися тим, чому потрібно враховувати таку поведінку в програмі, ІМО. Але це було б непогано, щоб я запропонував якийсь можливий дизайн.
DPM

10

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

             +----------------+                   +--------------------+
             |    Animal      |                   |      Food          |
             |----------------|<--+Interfaces+--->|--------------------|
             |                |                   |                    |
             +----------------+                   +--------------------+
                +           +                       +                 +
                |           |    Abstract Classes   |                 |
                |           |        |          |   |                 |
                v           v        v          v   v                 v
   +-----------------+  +----------------+     +------------+      +------------+
   |   Herbivore     |  |  Carnivore     |     |   Plant    |      |   Meat     |
   |-----------------|  |----------------|     |------------|      |------------|
   |Eat(Plant p)     |  |Eat(Meat m)     |     |            |      |            |
   |                 |  |                |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
            +                    +                    +                   +
            |                    |                    |                   |
            v                    v                    v                   v
   +-----------------+  +----------------+     +------------+      +------------+
   |  Deer           |  |   Lion         |     |  Grass     |      |  DeerMeat  |
   |-----------------|  |----------------|     |------------|      |------------|
   |DeerMeat Die()      |void Kill(Deer) |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
                                 ^                    ^
                                 |                    |
                                 |                    |
                              Concrete Classes -------+

Як бачите, вони обидва піддають метод їжі, але те, що вони їдять, змінюється. Лев тепер може вбити оленя, олень може померти і повернути DeerMeat, а ОР оригінальний питання про те, як дозволити леву з'їсти оленя, але не траву, відповісти без інженерії цілої екосистеми.

Звичайно, це стає цікавим дуже швидко, тому що оленів можна вважати і типом м'яса, але щоб все було просто, я створив би метод, який називається kill () під оленями, який повертає м'ясо оленів, і ставлю це як конкретний клас розширення м'яса.


Чи вдасться оленям відкрити інтерфейс IMeat?
Ден Пішельман

М’ясо не інтерфейс, це абстрактний клас. Я додав, як би це здійснив для вас
1313

Eat(Plant p)і Eat(Meat m)обидва порушують LSP.
Тулен Кордова

Як так @ user61852? Я навмисно не виставляв Eat в інтерфейс для тварин, щоб кожен тип тварин міг мати свій власний метод поїдання.
1313

1
TCWL (Занадто складний, просочиться). Проблема розподіляється та виникає, і ваше рішення є статичним, централізованим та заздалегідь визначеним. TCWL.
Тулен Кордова

7

Мій дизайн був би таким:

  1. Продукти харчування декларуються як інтерфейси; є інтерфейс IFood та два похідних інтерфейси від нього: IMeat та IVegetable
  2. Тварини реалізують IMeat, а овочі впроваджують IVegetable
  3. Тварини мають двох нащадків, м'ясоїдних та гебіворів
  4. У м’ясоїдних є метод Eat, який отримує екземпляр IMeat
  5. Травоїдні тварини мають метод Eat, який отримує екземпляр IVegetable
  6. Лев сходить від м’ясоїдних
  7. Олень сходить від травоїдних
  8. Трава сходить із Овочів

Оскільки тварини реалізують IMeat, а олень - тварина травоїдних, Лев, який є (м'ясоїдним) твариною, який може їсти IMeat, також може їсти оленів.

Олень є травоїдним, тому він може їсти траву, оскільки реалізує IVegetable.

Хижі тварини не можуть їсти їжу, а травоїдні не можуть їсти IMeat.


1
Я бачу тут багато типів перерахування, які використовують успадкування просто для обмеження, коли типи, які передаються у спадщину, нічого не реалізують ... Кожного разу, коли ви виявляєте, що робите типи, які взагалі не реалізують будь-яку функціональність, це передача щось нога; Ви розширили модель в системі типів, яка не дає ніякого значення юзабіліті в коді
Jimmy Hoffa

Пам’ятайте, чим всеїдні існують, як люди, мавпи та ведмеді.
Тулен Кордова

Тож як ви додасте, що і леви, і олені - ссавці? :-)
johannes

2
@JimmyHoffa Вони називаються "маркерні інтерфейси" і є абсолютно допустимим використанням інтерфейсу. Потрібно переглянути код, щоб визначити, чи використання виправдане, але є багато випадків використання (наприклад, цей, коли Лев, який намагається з'їсти Траву, кине виняток NoInterface). Інтерфейс маркера (або його відсутність) служить передбаченням виключення, яке буде викинуто, якщо метод викликається з непідтримуваними аргументами.
rwong

1
@rwong Я розумію цю концепцію, ніколи не чув, щоб вона була формалізована раніше; просто мій досвід був кожен раз, коли кодова база, над якою я працював, робить їх складнішими і складнішими в обслуговуванні. Можливо, мій досвід, однак, якраз був там, де люди неправильно їх використовували.
Джиммі Хоффа

5

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

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

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

Багатократне успадкування насправді теж не дуже добре вирішує це питання. Вам потрібна якась колекція речей, яку може їсти тварина, або тварин, які можуть їсти їжу.


Як кажуть про регулярне вираження "У мене була проблема, тому я використовував регулярний вираз, тепер у мене дві проблеми", ІМ - це "" У мене була проблема, тому я використовував ІМ, тепер у мене 99 проблем "Якби я був ти" Будьте слідом за тим, що ви даремно тикали сюди, хоча про їжу знаючи, що можна їсти, це фактично спрощує модель на тонну. Інверсія залежності FTW.
Джиммі Хоффа

1

Я підійду до проблеми з іншого боку: OOP - це про поведінку. У вашому випадку, чи Grassмає бути поведінка дитини Food? Тож у вашому випадку не буде Grassкласу, або, принаймні, він не буде успадкований від Food. Крім того, якщо вам потрібно встановити, хто може їсти те, що під час збирання, сумнівно, чи потрібна вам Animalабстракція. Крім того, не рідко можна побачити м’ясоїдів, які їдять траву , хоча і не для харчування.

Тож я б спроектував це як (не збираюся морочитися з мистецтвом ASCI):

IEdibleз властивістю Type, яка є перерахуванням м'яса, рослин, туші тощо. (це не змінюватиметься часто і не має конкретної поведінки, тому немає необхідності моделювати це як пошук класів).

Animalз методами CanEat(IEdible food)і Eat(IEdible food), які є логічними. Тоді конкретні тварини можуть перевіряти, коли потім можуть їсти дану їжу за певних обставин, а потім їсти цю їжу, щоб отримувати харчування / робити щось інше. Крім того, я б моделював класи м’ясоїдних, травоїдних, всеїдних як зразок стратегії , ніж як частину ієрархії тварин.


1

TL; DR: Дизайн або модель з контекстом.

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

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

Наприклад, більш детальні вимоги потребують різних об'єктних відносин:

  • підтримка Animals eatне FoodсхожихGastroliths
  • підтримка Chocolate як Poisonдля Dogs, але не дляHumans

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


Я погоджуюся, але його лише невеликий приклад - не намагалися моделювати світ. Наприклад, у вас може бути акула, яка їсть шини та номерні знаки. Ви можете просто зробити батьківський абстрактний клас методом, який їсть будь-який тип об'єкта, і Food може розширити цей клас абстрактів.
hagensoft

@hagensoft: Погоджено. Мені часом захоплюється, тому що я постійно бачу розробників, що моделюють на основі метафори, яку вони негайно захопили, а не дивляться на те, як потрібно використовувати та використовувати дані. Вони одружуються з дизайном OO, заснованим на початковій ідеї, а потім намагаються примусити проблему підходити до їхнього рішення, а не для того, щоб їх рішення підходило до проблеми.
дієтабудда

1

Підхід щодо складання над складом ECS:

An entity is a collection of components.
Systems process entities through their components.

Lion has claws and fangs as weapons.
Lion has meat as food.
Lion has a hunger for meat.
Lion has an affinity towards other lions.

Deer has antlers and hooves as weapons.
Deer has meat as food.
Deer has a hunger for plants.

Grass has plant as food.

Псевдокод:

lion = new Entity("Lion")
lion.put(new Claws)
lion.put(new Fangs)
lion.put(new Meat)
lion.put(new MeatHunger)
lion.put(new Affinity("Lion"))

deer = new Entity("Deer")
deer.put(new Antlers)
deer.put(new Hooves)
deer.put(new PlantHunger)

grass = new Entity("Grass")
grass.put(new Plant)

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

Nature({lion, deer, grass})

Nature(entities)
{
    for each entity in entities:
    {
       if entity.has("MeatHunger"):
           attack_meat(entity, entities.with("Meat", exclude = entity))
       if entity.has("PlantHunger"):
           eat_plants(entity, entites.with("Plant", exclude = entity))
    }
}

Можливо, ми хочемо розширити, Grassщоб мати потребу в сонячному світлі та воді, і ми хочемо внести сонячне світло та воду у наш світ. Але Grassвони не можуть їх шукати безпосередньо, як цього немає mobility. AnimalsМожливо, також потрібна вода, але вона може активно її шукати, оскільки є mobility. Досить просто продовжувати розширювати та змінювати цю модель без каскадних поломок всієї конструкції, оскільки ми просто додаємо нові компоненти та розширюємо поведінку наших систем (або кількості систем).


0

Як ви вирішите цю проблему, використовуючи багаторазове успадкування та використовуючи об'єктно-орієнтований дизайн?

Як і більшість речей, це залежить .

Це залежить від того, якою ви бачите «цю проблему».

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

Якщо ви запитуєте про загальну проблему впровадження, відповідь залежатиме від можливостей вашого оточення. Інтерфейси IFood і IAnimal можуть працювати, підклас EdibleAnimal реалізує обидва інтерфейси. Якщо ваше середовище не підтримує інтерфейси, просто перетворіть тварин у спадок від Food.

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

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


0

Спадкування слід використовувати для того, що завжди є чимось іншим, і не може змінитися. Трава - це не завжди їжа. Наприклад, я не їжу траву.

Трава грає роль продуктів харчування для певних тварин.


Це просто абстракція. Якщо це вимога, то ви можете створити більше підрозділів, які розширюватимуть клас абстрактних рослин і змушують людей їсти абстрактний клас на зразок "HumanEatablePlants", який би згрупував рослини, які їдять люди, у конкретні класи.
hagensoft

0

Ви щойно натрапили на основне обмеження ОО.

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

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

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

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

Оленем може бути домашня тварина, худоба, тварина в зоопарку або охоронюваний вид.

Лев також може бути твариною зоопарку або охоронюваним видом.

Життя не просте.


0

Як ви вирішите цю проблему, використовуючи багаторазове успадкування та використовуючи об'єктно-орієнтований дизайн?

Яка проблема? Що робить ця система? Поки ви не відповісте на це, я не маю поняття, які класи можуть бути потрібні. Чи намагаєтесь ви моделювати екологію з хижаками, травоїдними рослинами та рослинами, проектуючи популяції видів у майбутнє? Чи намагаєтесь ви мати комп’ютер відтворити 20 питань?

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

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