Чи повинні імена інтерфейсів починатися з префікса "I"?


73

Я читав " Чистий код " Роберта Мартіна, щоб сподіватися, що став кращим програмістом. Хоча жоден з них поки що не був переломним, це змусило мене думати інакше про те, як я розробляю програми та пишуть код.

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

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

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

Отже, моє запитання зводиться до "Чому ми повинні приховувати той факт, що якась частина коду очікує інтерфейс?"

Редагувати

У відповідь на відповідь:

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

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


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

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

2
Попереднє редагування заголовка було жахливим. Це питання не стосується угорського позначення взагалі просто тому, що в ньому згадується конвенція, яка може бути пов'язана з ним. Відносні достоїнства НН тут абсолютно не мають значення; питання стосувалося конкретно інтерфейсів проти класів та того, чи важливі / цікаві смислові відмінності для обґрунтування конвенції іменування у спеціальному випадку.
Aaronaught

Re to know whether they will be implementing an interface or extending a class: так, але більшість користувачів вашого коду буде називати його, а не реалізовувати його чи розширювати, і їм справді було неважливо, що це таке.
david.pfx

Що варто, я масово віддаю перевагу префіксу "Я". Я також використовую префікс "Анотація" на абстрактних класах з тієї ж причини. Це не має значення для споживачів класу / інтерфейсу, але може зробити велике значення для тих, кому потрібно надати його примірники, а також робить його набагато простішим для інших розробників, які читають ваш код. Це означає, що вони можуть з першого погляду бачити, з чим вони мають справу, замість того, щоб отримати додаткову інформацію для кожного конкретного випадку, звертаючись до IDE. Я тільки почав використовувати Angular і мені здається, що це дуже прикро, що вони не дотримуються цієї конвенції!
Дан Кінг

Відповіді:


66

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

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

Насправді найважливішими відмінностями між класами та інтерфейсами є:

  • Інтерфейси не можуть мати приватні дані;
  • Учасники інтерфейсу не можуть мати модифікаторів доступу (усі учасники є "загальнодоступними");
  • Клас може реалізовувати декілька інтерфейсів (на відміну від загальної можливості успадковувати лише один базовий клас).

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

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

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

Враховуючи це, я, як правило, префіксую свої C # інтерфейси в Iбудь-якому випадку, тому що це .NET-конвенція, яку використовує та обстоює Microsoft. І коли я пояснюю умовам кодування новому розробнику, набагато менше клопоту просто використовувати правила Microsoft, ніж пояснювати, чому у нас є свої "спеціальні" правила.


Дякую за цю поломку. +1 за "змістовні розрізнення класів та інтерфейсів ..."
Чарльз Спрейберрі

23
+1 для "Все це, що я говорив, я, як правило, префіксую свої C # інтерфейси з I так чи інакше, тому що це. Цього мені достатньо причини в C #. Дотримуючись цього загального стандарту, більш ймовірно, що інші .NET-програмісти з ідентифікацією інтерфейсу.
Робоцусі

4
Оскільки хтось, хто пише як Java, так і C #, тверджує: "Все це говориться, я, як правило, префіксую свої C # інтерфейси з I все-таки, тому що це. допомагає мені запам’ятати, на якій мові я працюю,
Aidos

3
Ще одна відмінність .NET (але не в Java) полягає в тому, що багато мов .NET не дозволяють інтерфейсам містити статичні методи, тому хакерство Iвідключеного інтерфейсу може дати хороше ім'я класу для проведення статичних методів, пов'язаних з інтерфейсом (наприклад, Enumerable<T>.Emptyабо Comparer<T>.Default).
supercat

У мене є одна турбота. Якщо ви відкриєте заголовок на C ++ і побачите, що він class Fooвже розширюється class Bar, це не відразу очевидно class Bar, розширюється чи реалізується. Тож тепер вам потрібно зайти і подивитися в class Barзаголовок, щоб вирішити, чи добре модифікувати, class Fooщоб продовжити class Baz. Крім того, префікс I дає очевидну візуальну підказку, якщо "єдиний базовий клас" порушується чи ні - тому ви отримуєте перевірку правильності кожного разу, коли відкриваєте заголовок.
jsj

14

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


2
+1 за послідовність> умовність. У C # ви б встановили префікс з I, а в Java ви не зробили б. Різні завдання вимагають різних конвенцій
Bringer128

2
+1, хоча я особисто вважаю за краще, щоб він не знаходився на моїх інтерфейсах, оскільки він не відповідав бібліотекам BCL та стороннім бібліотекам, які використовуються в проектах .Net - це не варіант.
jk.

13

Книга повна хороших речей, але я все одно додав би "Я" до назви інтерфейсу.

Уявіть, у вас є public interface ILogреалізація за замовчуванням public class Log.

Тепер, якщо ви вирішили це зробити public interface Log, раптом вам доведеться змінити клас журналу на public class LogDefaultщось таке в цих рядках. Ви перейшли від одного зайвого персонажа до семи - це, звичайно, марно.

Між теорією та практикою часто існує тонка грань. Теоретично це дійсно гарна ідея, але на практиці це не так добре.


Про це також згадується в документації
levininja

30
"Уявіть, що у вас є публічний інтерфейс ILog з журналом публічного класу реалізації за замовчуванням." - Тоді у вас неправильне ім'я для вашої реалізації за замовчуванням. Що робить Log? Увійдіть до консолі? Щоб систематизувати? Нікуди? Ті , слід назвати ConsoleLog, SyslogLogі NullLog, відповідно. Logне є хорошою назвою для конкретного класу, тому що це не конкретна назва.
Себастьян Редл

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

Ім'я класу використовується один раз (для екземпляра), ім'я інтерфейсу використовується скрізь. Додавання листа в інтерфейс для збереження літери на назві класу - помилкова економія (символів).
сергут

@ RichardTingle Але я думаю, що загальне використання там просто таке, що MyClassмає макетний варіант, правда? Тобто, ми часто використовуємо інтерфейси, щоб по суті зробити публічні методи справді відкритими таким чином, що дозволяє протестувати. (Не кажучи, що це добре чи погано, але розуміння того, що мотивація інтерфейсів часто полягає просто у тому, щоб зробити класи, які є залежними, легко піддаються обробці, пояснює відображення від 1 до 1 MyClassдля IMyClassвідображення.)
ruffin

8

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

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

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

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

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


@Mat> Так, це була моя помилка, і я її відредагував.
deadalnix

5

Здається, більшість відповідей припускають, що мова програмування - це або Java, або C #, де є концепція (або ключове слово) для інтерфейсів / абстрактних класів. У цих випадках в пристойному IDE відразу видно, з яким типом першого класу має справу і написання public interface IMyClass- непотрібне дублювання. Це як би ви не писали public final FinalMyClass- уявіть, щоб кожне ключове слово було введено в назву класів.

Однак, на мою думку, у C ++ ситуація дещо інша, оскільки треба зануритися у файл заголовка і побачити, чи є у класу якісь віртуальні методи, щоб з’ясувати, чи це абстрактний клас. У цьому випадку я чітко бачу аргумент для написання class AbstractMyClass.

Тож моя відповідь була б: Це залежить від мови програмування.

Оновлено 2016: 2 роки досвіду роботи на C ++ пізніше, я б зараз, мабуть, вважав би поганою практикою встановлювати префікси імен класів Abstract, хоча я б сказав, що, ймовірно, є кодові бази, які є настільки складними, що може мати сенс.


Ви впевнені, що це відповідає на питання? Ви можете прочитати інші відповіді та уточнити деякі свої моменти.
david.pfx

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

@ david.pfx: Я думаю, що я точно відповідаю на питання "Чи повинні імена інтерфейсів починатися з префікса" Я "?": Це залежить від мови програмування. Якщо ви вважаєте, що моя розробка недостатньо чітка, я рада її вдосконалити - які частини вам не зрозумілі?
Ela782

2
@JohnGaughan: Звичайно, у нього є інтерфейси! Але вся моя суть у тому, що мова йде про ключове слово. У C ++ його немає, тому IDE / користувачеві його не видно, чи має справу з інтерфейсом чи ні. На Java, однак, це відразу видно.
Ela782

1
Прочитайте відповідь на найвищу оцінку та порівняйте свою. Ви не новачок, і це можливість дізнатися, які відповіді приваблюють голоси. Як зараз, ваші не будуть. Маючи більше роботи, більше пояснень, більше цінності, це просто може. Тримайся там!
david.pfx

4

Дотримуйтесь ідіоми мови, якою ви користуєтесь.

Кожна мова має свої ідіоми та вказівки щодо кодування. Наприклад, у C # ідіоматично встановлювати префікси інтерфейсів із I. У Go, це не так.


+1 Дотримуйтесь ідіомів мови, якою ви користуєтесь. Очевидно, що автор книги - це розробник Java. .NET / C #: ILog Java: Журнал Але недолік: Коли мені подобається мати клас Log, який реалізує інтерфейс ILog .... MyLog, LogDefault, LogBase, ...? Потворне, чи не так? Більш потворне: LogImpl ....: D
hfrmobile

0

У своєму (Java) коді я схильний мати цю умову в API, які я піддаю абонентам:

  • Функціональність забезпечується через інтерфейси, і ніколи за класами.
  • Нефункціональні частини - це заняття.

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

Що стосується умов іменування, інтерфейси мають тенденцію до відображення або іменників дійових осіб (наприклад, FooBarWatcher), або прикметників (наприклад, FooBarWatchable), і чисті класи даних, і перерахування відображають неактивні іменники (наприклад, FooBarStatus); IDE може керувати споживачами API без спеціальних угод про іменування. Винятки слідують звичайним правилам Java ( FooBarException, FooBarError, FooBarFault) звичайно.

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


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