Я, мабуть, вважав би кодовим запахом або навіть антидіаграмою мати клас, який реалізує 23 інтерфейси. Якщо це дійсно антидіапазон, як би ви його назвали? Або просто не слідувати принципу єдиної відповідальності?
Я, мабуть, вважав би кодовим запахом або навіть антидіаграмою мати клас, який реалізує 23 інтерфейси. Якщо це дійсно антидіапазон, як би ви його назвали? Або просто не слідувати принципу єдиної відповідальності?
Відповіді:
Королівська родина: Вони не роблять нічого особливо шаленого, але вони мають мільярд назв і так чи інакше пов'язані з більшістю інших королівських роялів.
somehow
Я стверджую, що цей антидіапазон отримав назву Jack of All Trades, або, можливо, занадто багато капелюхів.
Якби мені довелося назвати це ім’я, я думаю, я би назвав його гідрою :
У грецькій міфології Лернейська гідра (грецька: Λερναία Ὕδρα) була старовинним безіменним змієм, схожим на хтонічного водного звіра, з рептилійськими рисами, (як називається його назва), що володіло багатьма головами - поети згадують більше голів, ніж вази-художники фарби, і на кожну відрізану голову виростало ще два - і отруйне дихання, таке вірулентне, навіть її сліди були смертельними.
Особливою актуальністю є той факт, що він не тільки має багато голів, але все більше зростає і їх неможливо вбити через це. Це був мій досвід роботи з подібними конструкціями; розробники просто продовжують заклинювати все більше і більше інтерфейсів до нього, поки у нього не надто багато вміститься на екрані, і до цього часу він настільки глибоко закріпився в дизайні програми та припущеннях, що розщеплення її - безперспективна перспектива (якщо ви спробуєте, ви насправді часто в кінцевому підсумку потрібно більше інтерфейсів для усунення розриву).
Одним з ранніх ознак майбутньої приреченості через "гідру" є часте передавання одного інтерфейсу на інший, без особливої перевірки розумності, а часто до цілі, яка не має сенсу, як у:
public void CreateWidget(IPartLocator locator, int widgetTypeId)
{
var partsNeeded = locator.GetPartsForWidget(widgetTypeId);
return ((IAssembler)locator).BuildWidget(partsNeeded);
}
Коли виходити з контексту, очевидно, що в цьому коді є щось рибне, але ймовірність того, що це станеться, зростає з тим, що більше "голів" об'єкта зростає, оскільки розробники інтуїтивно знають, що вони завжди мають справу з однією і тією ж істотою.
Удачі коли-небудь робити технічне обслуговування об’єкта, який реалізує 23 інтерфейси. Шанси, що ви не заподієте побічну шкоду в процесі, незначні.
Бог Об'єкт приходить на розум; єдиний об’єкт, який вміє робити ВСЕ. Це пов'язано з низьким дотриманням вимог "згуртованості" двох основних методологій проектування; якщо у вас є об'єкт з 23 інтерфейсами, у вас є об'єкт, який вміє бути 23 різними речами для його користувачів, і ці 23 різні речі, ймовірно, не уздовж однієї задачі або області системи.
У SOLID, хоча творець цього об’єкта, очевидно, намагався слідувати принципу розбиття інтерфейсу, вони порушили попереднє правило; Принцип єдиної відповідальності. Є причина, що це "СОЛІД"; S завжди стає першим, коли думає про дизайн, а всі інші правила дотримуються.
У GRASP творець такого класу нехтував правилом "Високої згуртованості"; GRASP, на відміну від SOLID, вчить, що об’єкт не повинен мати єдиної відповідальності, але, максимум, він повинен мати дві-три дуже тісно пов'язані між собою обов'язки.
23 - Просто число! У найімовірнішому сценарії це достатньо високий рівень тривоги. Однак якщо ми запитаємо, яка найбільша кількість методів / інтерфейсів, перш ніж вона може отримати тег, який можна назвати "антидіаграмою"? Це 5 чи 10 чи 25? Ви розумієте, що це число насправді не є відповіддю, тому що якщо 10 добре, 11 також може бути - і тоді будь-яке ціле число після цього.
Справжнє питання полягає у складності. І ми повинні зазначити, що тривалий код, кількість методів або великий розмір класу за будь-якими заходами насправді НЕ є визначенням складності. Так, більший і більший код (більша кількість методів) утрудняє читання та розуміння нового новачка. Він також обробляє потенційно різноманітну функціональність, велику кількість винятків та досить розвинені алгоритми для різних сценаріїв. Це не означає, що він складний - його важко засвоювати.
З іншого боку, порівняно невеликий розмір коду, який можна сподіватися прочитати через кілька годин, і ще може бути складним. Ось, коли я думаю, що код є (зайвим) складним.
Кожен фрагмент мудрості об'єктно-орієнтованого дизайну можна поставити тут, щоб визначити "складний", але я би обмежився тут, щоб показати, коли "так багато методів" є ознакою складності.
Взаємні знання. (він же з'єднання) Багато разів, коли речі записуються як класи, ми всі вважаємо, що це "приємний" об'єктно-орієнтований код. Але припущення про інший клас по суті порушує реальну необхідну інкапсуляцію. Коли у вас є методи, які "просочують" глибокі деталі про внутрішній стан алгоритмів - і додаток будується з ключовим припущенням про внутрішній стан обслуговуючого класу.
Занадто багато повторень (проривів), коли методи, які мають схожі назви, але виконують суперечливі дії, - або суперечать іменам із подібною функціональністю. Багато разів коди розвиваються для підтримки трохи різних інтерфейсів для різних додатків.
Занадто багато ролей Коли клас продовжує додавати бічні функції і продовжувати розширюватися більше, як люблять люди, лише знаючи, що зараз клас - це два класи. Дивно, але все це починається з справжньоговимог, і для цього не існує жодного іншого класу. Враховуючи це, існує клас Transaction, який розповідає деталі транзакції. Наразі добре виглядає, тепер комусь потрібна конверсія формату на "час трансакції" (між UTC та подібним чином), пізніше люди додають правила, щоб перевірити, чи була певна річ у певні дати для перевірки недійсних транзакцій. - Я не буду писати всю історію, але, врешті-решт, клас транзакцій будує весь календар з ним, і тоді люди починають використовувати «лише календарі» його частину! Це дуже складно (уявіть собі), чому я буду створювати "клас транзакцій", щоб мати функціонал, який би міг надати "календаря"!
(Не) узгодженість API Коли я роблю book_a_ticket () - я бронюю квиток! Це дуже просто, незалежно від того, скільки перевірок і процесів потрібно здійснити, щоб це відбулося. Тепер це стає складним, коли потік часу починає це робити. Як правило, можна дозволити "пошук" та можливий доступний / недоступний статус, тоді для зменшення зворотного n-вперед ви починаєте зберігати деякі контекстні покажчики всередині квитка, а потім звертаєтесь до цього, щоб забронювати квиток. Пошук - не єдина функціональність - все стає гірше після багатьох таких "побічних функцій". У процесі сенс book_a_ticket () означає, що book_that_ticket ()! і це може бути немислимо складним.
Напевно, існує багато таких ситуацій, які ви побачите в дуже розвиненому коді, і я впевнений, що багато людей можуть додати сценарії, де "так багато методів" просто не має сенсу або не робить те, що ви, очевидно, думаєте. Це анти-модель.
Мій особистий досвід полягає в тому, що коли проекти, які починаються розумно знизу, багато речей, для яких мали існувати справжні класи самостійно - залишаються похованими або ще гіршими, все ще залишаються розщепленими між різними класами, а зв'язок збільшується. Найчастіше те, що заслуговувало б 10 занять, але їх було лише 4, цілком ймовірно, що багато з них мають багатоцільові заплутаність та велику кількість методів. Назвіть це БОГ і ДРАГОН, це те, що БАГ.
Але ви натрапляєте на ДУЖЕ ВЕЛИЧІ класи, які чітко відповідають, у них є 30 методів - і все ще дуже ЧИСТО. Вони можуть бути хорошими.
Класи повинні мати точно потрібну кількість інтерфейсів; ні більше, ні менше.
Сказати "занадто багато" було б неможливо, не дивлячись, чи всі ці інтерфейси служать корисній цілі в цьому класі. Заявлення про те, що об’єкт реалізує інтерфейс, означає, що вам доведеться реалізувати його методи, якщо ви очікуєте компіляції класу. (Я припускаю, що клас ви дивитесь.) Якщо все в інтерфейсі реалізовано, і ці реалізації реалізують щось, що стосується внутрішніх частин класу, важко буде сказати, що впровадження цього не повинно бути. Єдиний випадок, який я можу придумати, де інтерфейс, що відповідає цим критеріям, не належав би там, є зовнішнім: коли його ніхто не використовує.
Деякі мови дозволяють інтерфейси "підкласифікувати" за допомогою такого механізму, як extends
ключове слово Java , і той, хто його написав, може не знати цього. Можливо, також, що всі 23 є достатньо віддаленими, що їх об'єднання не має сенсу.
Звучить так, як вони думали, що для кожної властивості є пара "getter" / "setter", як це нормально для типового "bean" і рекламує всі ці методи в інтерфейсі. Отже, як щодо того, щоб називати це " бобом ". Або більш раблейський "Метеоризм" після добре відомих афектів занадто багато бобів.
Кількість інтерфейсів часто зростає, коли загальний об'єкт використовується в різних середовищах. У C # варіанти IComparable, IEqualityComparer та IComparer дозволяють сортувати за різними налаштуваннями, тож ви можете в кінцевому підсумку реалізувати всі, деякі з них можуть бути не раз, оскільки ви можете реалізувати загальнодоступні версії, що сильно набираються, а також негенеричні версії , крім того, ви можете реалізувати більше ніж один із загальних програм.
Візьмемо для прикладу сценарій, скажімо, веб-магазин, де ви можете придбати кредити, за допомогою яких ви можете придбати щось інше (сайти з фотографіями часто використовують цю схему). У вас може бути клас "Валута" і клас "Кредити", які успадковують одну і ту ж базу. У Валути є кілька чудових операційних перевантажень та порівняльних процедур, які дозволяють робити розрахунки, не турбуючись про властивість "Валюта" (додаючи фунти до доларів, наприклад). Кредити виділяються простіше, але мають деякі інші особливості поведінки. Бажаючи мати можливість порівнювати їх між собою, ви можете в кінцевому підсумку впровадити IComparable, а також IComparable та інші варіанти інтерфейсів порівняння на обох (навіть якщо вони використовують загальну реалізацію, будь то в базовому класі чи десь ще).
Під час реалізації серіалізації реалізуються ISerializable, IDeserializationCallback. Потім реалізуємо скасування повторення стеків: додається IClonable. Функція IsDirty: IObservable, INotifyPropertyChanged. Дозволити користувачам редагувати значення за допомогою рядків: IConvertable ... Список можна продовжувати і вмикати ...
У сучасних мовах ми бачимо іншу тенденцію, яка допомагає розділити ці аспекти та розмістити їх у власних класах, поза основним класом. Потім зовнішній клас або аспект асоціюється з цільовим класом за допомогою анотації (атрибутів). Часто можна зробити класи зовнішнього аспекту більш-менш загальними.
Використання атрибутів (анотація) підлягає роздумуванню. Один недолік - незначні (початкові) втрати продуктивності. Недоліком (часто емоційним) є те, що такі принципи, як інкапсуляція, повинні бути послаблені.
Завжди є інші рішення, але для кожного витонченого рішення є компроміс чи улов. Наприклад, використання рішення ORM може вимагати, щоб усі властивості були оголошені віртуальними. Рішення серіалізації можуть вимагати конструкторів за замовчуванням у ваших класах. Якщо ви використовуєте ін'єкцію залежностей, можливо, в результаті реалізуєте 23 інтерфейси для одного класу.
На мої очі, 23 інтерфейси не повинні бути поганими за визначенням. За нею може бути добре продумана схема, або якась принципова детермінація, наприклад, уникнення використання рефлексії чи екстремальних переконань щодо капсулювання.
Кожен раз, коли перемикаєте роботу або доводиться будувати існуючу архітектуру. Моя порада спочатку ознайомитись повністю, не намагайтеся переробляти все занадто швидко. Послухайте оригінального розробника (якщо він все ще є) і спробуйте з'ясувати думки та ідеї, що стоять за побаченим. Задаючи питання, робіть це не заради того, щоб його зламати, а для того, щоб навчитися ... Так, у кожного є свої золоті молотки, але чим більше молотків ви зможете зібрати, тим простіше вам станеться з колегами по колезі.
"Занадто багато" є суб'єктивним: стиль програмування? виконання? відповідність стандартам? прецеденти? просто відчуття комфорту / впевненості?
Поки ваш код поводиться правильно і не створює жодних проблем щодо ремонту, 23 це може бути навіть новою нормою. Колись я можу сказати: "Ви можете зробити чудову роботу з 23 інтерфейсами, див.: Jonas Elfström".
Я б сказав, що обмеження має бути меншим числом, ніж 23 - більше, як 5 або 7,
однак це не включає будь-яку кількість інтерфейсів, які ці інтерфейси успадковують, або будь-яку кількість інтерфейсів, реалізованих базовими класами.
(Отже, N + будь-яка кількість успадкованих інтерфейсів, де N <= 7.)
Якщо ваш клас реалізує занадто багато інтерфейсів, це, ймовірно, клас богів .