Скільки занадто багато інтерфейсів у класі? [зачинено]


28

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


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

Клас, про який йде мова, є різновидом сутності, а інтерфейси - це здебільшого властивості. Існує 113 таких і лише чотири методи.
Йонас Ельфстрем

6
Я думав, що інтерфейси по суті використовуються для введення качок на статично складених мовах. В чому проблема? :)
ashes999

2
лише запитання: чи всі 113 властивостей заповнені для кожного примірника цього об'єкта? Або це якась "спільна сутність", що використовується в різних контекстах із заповненими лише деякими властивостями?
Девід

1
Повернемося до концептуального розуміння того, що являє собою "клас". Клас - це єдина річ з чітко визначеною відповідальністю та роллю. Чи можете ви визначити свій об'єкт 23 інтерфейсу таким чином? Чи можете ви опублікувати одне (нежойовське) речення, яке адекватно резюмує ваш клас? Якщо так, то 23 інтерфейси чудово. Якщо ні, то вона занадто велика, занадто складна.
Стівен Гросс

Відповіді:



23

Я стверджую, що цей антидіапазон отримав назву Jack of All Trades, або, можливо, занадто багато капелюхів.


2
Як щодо швейцарського армійського ножа?
FrustratedWithFormsDesigner

Здається, цей термін вже використовується для інтерфейсів із занадто великою кількістю методів :-) Хоча це теж має сенс і тут.
Jalayn

1
@FrustratedWithFormsDesigner Швейцарський армійський нож ніколи не матиме поганого смаку мати інтерфейс під назвою IValue, який містить 30 властивостей, з яких 20 передує новому ключовому слову модифікатора.
Jonas Elfström

3
+1 за занадто багато капелюхів ... щоб правильно приховати багатоголове божество
ньютопський

1
Швейцарський армійський ніж - це модель повторного використання. тобто одне "лезо" може виконувати методи, тому його, мабуть, погана аналогія!
Джеймс Андерсон

17

Якби мені довелося назвати це ім’я, я думаю, я би назвав його гідрою :

Гідра

У грецькій міфології Лернейська гідра (грецька: Λερναία Ὕδρα) була старовинним безіменним змієм, схожим на хтонічного водного звіра, з рептилійськими рисами, (як називається його назва), що володіло багатьма головами - поети згадують більше голів, ніж вази-художники фарби, і на кожну відрізану голову виростало ще два - і отруйне дихання, таке вірулентне, навіть її сліди були смертельними.

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

Одним з ранніх ознак майбутньої приреченості через "гідру" є часте передавання одного інтерфейсу на інший, без особливої ​​перевірки розумності, а часто до цілі, яка не має сенсу, як у:

public void CreateWidget(IPartLocator locator, int widgetTypeId)
{
    var partsNeeded = locator.GetPartsForWidget(widgetTypeId);
    return ((IAssembler)locator).BuildWidget(partsNeeded);
}

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

Удачі коли-небудь робити технічне обслуговування об’єкта, який реалізує 23 інтерфейси. Шанси, що ви не заподієте побічну шкоду в процесі, незначні.


16

Бог Об'єкт приходить на розум; єдиний об’єкт, який вміє робити ВСЕ. Це пов'язано з низьким дотриманням вимог "згуртованості" двох основних методологій проектування; якщо у вас є об'єкт з 23 інтерфейсами, у вас є об'єкт, який вміє бути 23 різними речами для його користувачів, і ці 23 різні речі, ймовірно, не уздовж однієї задачі або області системи.

У SOLID, хоча творець цього об’єкта, очевидно, намагався слідувати принципу розбиття інтерфейсу, вони порушили попереднє правило; Принцип єдиної відповідальності. Є причина, що це "СОЛІД"; S завжди стає першим, коли думає про дизайн, а всі інші правила дотримуються.

У GRASP творець такого класу нехтував правилом "Високої згуртованості"; GRASP, на відміну від SOLID, вчить, що об’єкт не повинен мати єдиної відповідальності, але, максимум, він повинен мати дві-три дуже тісно пов'язані між собою обов'язки.


Я не впевнений, що це антитіла God Object, оскільки Бог не обмежений жодними інтерфейсами. Обмеження такою кількістю інтерфейсів може зменшити всемогутність Бога. ;) Можливо, це об’єкт Химери?
FrustratedWithFormsDesigner

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

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

3
Боляче, що ти думаєш, що саме я створив клас.
Йонас Ельфстрем

Використання "ви" - це більш множина, яке стосується вашої команди розробників. ЯКЩО в цій команді, минулому чи теперішньому, створили цей клас. Я редагую, хоча.
KeithS

8

23 - Просто число! У найімовірнішому сценарії це достатньо високий рівень тривоги. Однак якщо ми запитаємо, яка найбільша кількість методів / інтерфейсів, перш ніж вона може отримати тег, який можна назвати "антидіаграмою"? Це 5 чи 10 чи 25? Ви розумієте, що це число насправді не є відповіддю, тому що якщо 10 добре, 11 також може бути - і тоді будь-яке ціле число після цього.

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

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

Кожен фрагмент мудрості об'єктно-орієнтованого дизайну можна поставити тут, щоб визначити "складний", але я би обмежився тут, щоб показати, коли "так багато методів" є ознакою складності.

  1. Взаємні знання. (він же з'єднання) Багато разів, коли речі записуються як класи, ми всі вважаємо, що це "приємний" об'єктно-орієнтований код. Але припущення про інший клас по суті порушує реальну необхідну інкапсуляцію. Коли у вас є методи, які "просочують" глибокі деталі про внутрішній стан алгоритмів - і додаток будується з ключовим припущенням про внутрішній стан обслуговуючого класу.

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

  3. Занадто багато ролей Коли клас продовжує додавати бічні функції і продовжувати розширюватися більше, як люблять люди, лише знаючи, що зараз клас - це два класи. Дивно, але все це починається з справжньоговимог, і для цього не існує жодного іншого класу. Враховуючи це, існує клас Transaction, який розповідає деталі транзакції. Наразі добре виглядає, тепер комусь потрібна конверсія формату на "час трансакції" (між UTC та подібним чином), пізніше люди додають правила, щоб перевірити, чи була певна річ у певні дати для перевірки недійсних транзакцій. - Я не буду писати всю історію, але, врешті-решт, клас транзакцій будує весь календар з ним, і тоді люди починають використовувати «лише календарі» його частину! Це дуже складно (уявіть собі), чому я буду створювати "клас транзакцій", щоб мати функціонал, який би міг надати "календаря"!

  4. (Не) узгодженість API Коли я роблю book_a_ticket () - я бронюю квиток! Це дуже просто, незалежно від того, скільки перевірок і процесів потрібно здійснити, щоб це відбулося. Тепер це стає складним, коли потік часу починає це робити. Як правило, можна дозволити "пошук" та можливий доступний / недоступний статус, тоді для зменшення зворотного n-вперед ви починаєте зберігати деякі контекстні покажчики всередині квитка, а потім звертаєтесь до цього, щоб забронювати квиток. Пошук - не єдина функціональність - все стає гірше після багатьох таких "побічних функцій". У процесі сенс book_a_ticket () означає, що book_that_ticket ()! і це може бути немислимо складним.

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

Мій особистий досвід полягає в тому, що коли проекти, які починаються розумно знизу, багато речей, для яких мали існувати справжні класи самостійно - залишаються похованими або ще гіршими, все ще залишаються розщепленими між різними класами, а зв'язок збільшується. Найчастіше те, що заслуговувало б 10 занять, але їх було лише 4, цілком ймовірно, що багато з них мають багатоцільові заплутаність та велику кількість методів. Назвіть це БОГ і ДРАГОН, це те, що БАГ.

Але ви натрапляєте на ДУЖЕ ВЕЛИЧІ класи, які чітко відповідають, у них є 30 методів - і все ще дуже ЧИСТО. Вони можуть бути хорошими.


Клас запитань має 23 інтерфейси, і він містить загалом 113 загальнодоступних властивостей та чотири методи. Лише деякі з них читаються лише. C # має властивості, реалізовані автоматично, тому я відрізняюся між методами та властивостями msdn.microsoft.com/en-us/library/bb384054.aspx
Jonas Elfström

7

Класи повинні мати точно потрібну кількість інтерфейсів; ні більше, ні менше.

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

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


Я вважав, що це питання є досить агностичним мовою. Фактичний код знаходиться в C #.
Jonas Elfström

Я ще не робив жодного C #, але, здається, підтримує подібну форму успадкування інтерфейсу.
Blrfl

Так, інтерфейс може «успадкувати» інші інтерфейси тощо.
Jonas Elfström

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

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

1

Звучить так, як вони думали, що для кожної властивості є пара "getter" / "setter", як це нормально для типового "bean" і рекламує всі ці методи в інтерфейсі. Отже, як щодо того, щоб називати це " бобом ". Або більш раблейський "Метеоризм" після добре відомих афектів занадто багато бобів.


Він знаходиться в C # і має властивості, реалізовані автоматично. Ці 23 інтерфейси, загалом, містять 113 таких властивостей.
Jonas Elfström

1

Кількість інтерфейсів часто зростає, коли загальний об'єкт використовується в різних середовищах. У C # варіанти IComparable, IEqualityComparer та IComparer дозволяють сортувати за різними налаштуваннями, тож ви можете в кінцевому підсумку реалізувати всі, деякі з них можуть бути не раз, оскільки ви можете реалізувати загальнодоступні версії, що сильно набираються, а також негенеричні версії , крім того, ви можете реалізувати більше ніж один із загальних програм.

Візьмемо для прикладу сценарій, скажімо, веб-магазин, де ви можете придбати кредити, за допомогою яких ви можете придбати щось інше (сайти з фотографіями часто використовують цю схему). У вас може бути клас "Валута" і клас "Кредити", які успадковують одну і ту ж базу. У Валути є кілька чудових операційних перевантажень та порівняльних процедур, які дозволяють робити розрахунки, не турбуючись про властивість "Валюта" (додаючи фунти до доларів, наприклад). Кредити виділяються простіше, але мають деякі інші особливості поведінки. Бажаючи мати можливість порівнювати їх між собою, ви можете в кінцевому підсумку впровадити IComparable, а також IComparable та інші варіанти інтерфейсів порівняння на обох (навіть якщо вони використовують загальну реалізацію, будь то в базовому класі чи десь ще).

Під час реалізації серіалізації реалізуються ISerializable, IDeserializationCallback. Потім реалізуємо скасування повторення стеків: додається IClonable. Функція IsDirty: IObservable, INotifyPropertyChanged. Дозволити користувачам редагувати значення за допомогою рядків: IConvertable ... Список можна продовжувати і вмикати ...

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

Використання атрибутів (анотація) підлягає роздумуванню. Один недолік - незначні (початкові) втрати продуктивності. Недоліком (часто емоційним) є те, що такі принципи, як інкапсуляція, повинні бути послаблені.

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

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

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


0

"Занадто багато" є суб'єктивним: стиль програмування? виконання? відповідність стандартам? прецеденти? просто відчуття комфорту / впевненості?

Поки ваш код поводиться правильно і не створює жодних проблем щодо ремонту, 23 це може бути навіть новою нормою. Колись я можу сказати: "Ви можете зробити чудову роботу з 23 інтерфейсами, див.: Jonas Elfström".


0

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

(Отже, N + будь-яка кількість успадкованих інтерфейсів, де N <= 7.)

Якщо ваш клас реалізує занадто багато інтерфейсів, це, ймовірно, клас богів .

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