Тут є одна крихітна різниця між Java та C #, яка є актуальною. У Java кожен член за замовчуванням віртуальний. У C # кожен член за замовчуванням опечатується, за винятком членів інтерфейсу.
Припущення, що впливають на це, керуються принципом - на Яві кожен публічний тип слід вважати не остаточним, відповідно до принципу заміни Ліскова [1]. Якщо у вас є лише одна реалізація, ви назвете клас Parser
; якщо ви виявите, що вам потрібно кілька реалізацій, ви просто поміняєте клас на інтерфейс з тим самим іменем і перейменовуєте конкретну реалізацію на щось описове.
У C # головне припущення полягає в тому, що коли ви отримуєте клас (ім'я не починається з I
), це клас, який ви хочете. Зауважте, це ніде не є на 100% точним - типовим зустрічним прикладом можуть бути класи типу Stream
(які насправді мали бути інтерфейсом або парою інтерфейсів), і кожен має свої вказівки та фони з інших мов. Існують також інші винятки, такі як досить широко використовуваний Base
суфікс для позначення абстрактного класу - як і в інтерфейсі, ви знаєте, що тип повинен бути поліморфним.
Існує також приємна зручність використання в тому, щоб залишити не-I-префікс ім'я для функціональності, що стосується цього інтерфейсу, не вдаючись до того, щоб зробити інтерфейс абстрактним класом (що зашкодить через відсутність багатократного успадкування класу в C #). Це було популяризовано LINQ, який використовує IEnumerable<T>
як інтерфейс, так і Enumerable
як сховище методів, що застосовуються до цього інтерфейсу. Це не потрібно в Java, де інтерфейси можуть містити також реалізацію методів.
Зрештою, I
префікс широко використовується у світі C #, а в розширенні - у світі .NET (оскільки на сьогоднішній день більшість коду .NET написано на C #, є сенс дотримуватися рекомендацій C # для більшості публічних інтерфейсів). Це означає, що ви майже напевно будете працювати з бібліотеками та кодом, що слідує за цим позначенням, і є сенс прийняти традицію, щоб запобігти зайвій плутанині - це не так, як якщо опущення префікса зробить ваш код краще :)
Я припускаю, що міркування дядька Боба були приблизно таким:
IBanana
є абстрактним поняттям банан. Якщо може бути будь-який клас-реалізатор, який би не мав кращої назви, ніж Banana
абстракція абсолютно безглузда, і вам слід кинути інтерфейс і просто скористатися класом. Якщо є краща назва (скажімо, LongBanana
або AppleBanana
), немає причин не використовувати Banana
як ім'я інтерфейсу. Тому використання I
префікса означає, що у вас є марна абстракція, що робить код важче зрозуміти без користі. І оскільки суворий OOP завжди буде вам кодувати проти інтерфейсів, єдине місце, де ви б не побачили I
префікс типу, було б на конструкторі - досить безглуздий шум.
Якщо застосувати це до зразкового IParser
інтерфейсу, ви можете чітко бачити, що абстракція повністю знаходиться на "безглуздій" території. Або є що - то конкретне про конкретну реалізацію синтаксичного аналізатора (наприклад JsonParser
, XmlParser
...), або ви повинні просто використовувати клас. Не існує такого поняття, як "реалізація за замовчуванням" (хоча в деяких середовищах це дійсно має сенс - особливо, COM), або існує конкретна реалізація, або ви хочете абстрактний клас або методи розширення для "за замовчуванням". Однак у C #, якщо ваша кодова база вже не містить I
-префікса, зберігайте її. Просто робіть class Something: ISomething
міркування щоразу, коли побачите код на зразок - це означає, що хтось не дуже добре стежить за YAGNI та будує розумні абстракції.
[1] - Технічно це не конкретно згадується в роботі Ліскова, але це одна з основ оригінальної статті OOP, і в моєму читанні Ліскова вона цього не оскаржувала. У менш суворій інтерпретації (той, який бере більшість мов OOP), це означає, що будь-який код, що використовує загальнодоступний тип, призначений для заміни (тобто не остаточний / запечатаний), повинен працювати з будь-якою відповідною реалізацією цього типу.