Це поганий дизайн OOP для моделювання, що включає інтерфейси?


13

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

( Я все ще резюмую тут і ще не маю реалізації коду, тому це швидше питання дизайну OOP ... Я думаю!)

Чи правильно я шукаю «спільну поведінку» між цими класами та реалізую їх як інтерфейси ?

Наприклад, вампіри та вовки кусають ... тож я повинен мати інтерфейс для укусів?

public class Vampire : Villain, IBite, IMove, IAttack

Так само і для вантажних автомобілів ...

public class Truck : Vehicle, IMove

А для людей ...

public class Man : Human, IMove, IDead

Чи тут моє мислення? (Вдячний за вашу допомогу)


14
Тварини, овочі та мінерали рідко дають хороші приклади для впровадження програм. Фактичні реалізації , як правило , більш абстрактно, як IEnumerable, IEquatableі т.д.
Роберт Харві

6
Ви маєте одну згадку про те, що збираєтеся робити ваші об’єкти у вашому програмному забезпеченні ("кусати"). Програмне забезпечення, як правило, розроблене для того, щоб щось робити , базуючи лише об'єктну модель на характеристиках, нікуди не веде.
tofro

@tofro Мій намір полягав у тому, що IBite міститиме декілька методів, які реалізовуватимуть поведінку стосовно (1) зниження рівня "життя / енергії" іншого (2) поява або виклик "кров'яної" графіки та (3) оновлення статики імітації дані (наприклад, NoOfBites). Я думаю, що я можу оцінити, що інтерфейс найкраще використовувати для реалізації різних методів поведінки.
користувач3396486

2
Чи не класи класів Human, Vampire та Vehicle вже реалізують інтерфейс IMove? Чому потрібно змусити підкласи реалізовувати це занадто виразно?
П'єр Арло

Чи потрібні всі ці інтерфейси? У Python, на щастя, вам не потрібен жоден із цих матеріалів, це було справді освіжаючою зміною (моєю першою мовою був Object Pascal). Також віртуальні методи можуть бути кращим рішенням у деяких випадках.
Ajasja

Відповіді:


33

Загалом, ви хочете мати інтерфейси для загальних характеристик вашого clasess.

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

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

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

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


1
Я, звичайно, сподіваюся, що кожен об'єкт не має тисяч атрибутів.
садок

4
Атрибути не такі, як у атрибутах oop, а граматичні атрибути / характеристики (такі, як численні, порівнянні тощо): D. Поганий вибір слів.
Paul92

3
Варто зазначити, що корисні інтерфейси - це ті, які ви будете використовувати. Наприклад, IBiteне особливо корисно, але, можливо, ви хочете, IAttackщоб ви могли працювати над усіма речами, що здійснюють атаки, або IUpdateтак, ви можете запускати оновлення для всього, або IPhysicsEnabledтак, ви можете застосовувати до них фізику тощо.
anaximander

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

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

29

Схоже, ви створюєте купу інтерфейсів одного методу . Це добре на обличчі, але пам’ятайте, що інтерфейси не належать класам / особам, які їх реалізують. Вони належать клієнтам, які ними користуються. Клієнти вирішують, чи потрібно щось робити, що може рухатись та атакувати.

Якщо у мене є Combatклас із fight()методом, цей метод, ймовірно, має потребу викликати move()і attack()той самий об’єкт. Це настійно говорить про необхідність використання ICombatantінтерфейсу, який fight()може телефонувати move()і attack()через. Це чистіше, ніж fight()взяти IAttackоб’єкт і кинути його, IMoveщоб побачити, чи він також може рухатися.

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

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


1
Чорт, це добре. Ігри просто здаються дійсно хорошим способом використання та пояснення OOP.
JeffO

7
@JeffO, поки ви фактично не реалізуєте досить велику гру і не зрозумієте, що OOP - це гарячий безлад, і вам буде краще з компонентними системами або орієнтованими на дані конструкціями.
Darkhogg

"Інтерфейси належать клієнтам, які ними користуються"
Tibos


1
+1 за різницю між бібліотеками та додатком, я часто (занадто багато?: /) Читаю багато речей, які просто підходять для однієї, а не для іншої.
Вальфрат

3

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

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

int total = 0;
foreach (object it in creatures)
{
   IWoozleCountable w = trycast(it, IWoozleCountable);
   if (w != null) total += w.WoozleCount;
}

Якщо ж WoozleCount був членом Creature / ICreature, хоча кілька підтипів перекривали б реалізацію WoozleCount за замовчуванням, яка завжди повертає нуль, код можна було б спростити до:

int total = 0;
foreach (ICreature it in creatures)
   total += it.WoozleCount;

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

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