Есть одно крошечное различие между Java и C #, которое уместно здесь. В Java каждый член по умолчанию является виртуальным. В C # каждый элемент запечатан по умолчанию - за исключением элементов интерфейса.
Предположения, которые идут с этим, влияют на руководство - в Java каждый открытый тип должен считаться не окончательным, в соответствии с принципом замещения Лискова [1]. Если у вас есть только одна реализация, вы назовете класс Parser
; если вы обнаружите, что вам нужно несколько реализаций, вы просто измените класс на интерфейс с тем же именем и переименуете конкретную реализацию в нечто описательное.
В C # главное предположение состоит в том, что когда вы получаете класс (имя не начинается с I
), это тот класс, который вам нужен. Имейте в виду, что это далеко не на 100% точно - типичным контрпримером были бы классы вроде Stream
(которые действительно должны были быть интерфейсом или парой интерфейсов), и у каждого были свои собственные рекомендации и фоны из других языков. Есть и другие исключения, такие как довольно широко используемый Base
суффикс для обозначения абстрактного класса - как и в случае с интерфейсом, вы знаете, что тип должен быть полиморфным.
Есть также хорошая удобная функция, позволяющая оставить имя без префикса для функциональности, относящейся к этому интерфейсу, не прибегая к тому, чтобы сделать интерфейс абстрактным классом (что повредит из-за отсутствия множественного наследования классов в C #). Это было популяризировано LINQ, который использует IEnumerable<T>
в качестве интерфейса и Enumerable
в качестве хранилища методов, которые применяются к этому интерфейсу. Это не нужно в Java, где интерфейсы также могут содержать реализации методов.
В конечном счете, I
префикс широко используется в мире C # и, соответственно, в мире .NET (поскольку большая часть кода .NET написана на C #, имеет смысл следовать рекомендациям C # для большинства общедоступных интерфейсов). Это означает, что вы почти наверняка будете работать с библиотеками и кодом, который следует этой нотации, и имеет смысл принять традицию, чтобы предотвратить ненужную путаницу - это не то же самое, что пропуск префикса сделает ваш код лучше :)
Я предполагаю, что рассуждения дяди Боба были примерно такими:
IBanana
это абстрактное понятие банана. Если может быть какой-либо реализующий класс, имя которого было бы не лучше, чем Banana
абстракция, то абстракция совершенно бессмысленна, и вам следует отказаться от интерфейса и просто использовать класс. Если есть лучшее имя (скажем, LongBanana
или AppleBanana
), нет причин не использовать его Banana
в качестве имени интерфейса. Следовательно, использование I
префикса означает, что у вас есть бесполезная абстракция, что делает код сложнее для понимания без какой-либо выгоды. А так как строгий ООП всегда заставит вас кодировать интерфейсы, единственное место, где вы не увидите I
префикса для типа, это конструктор - это совершенно бессмысленный шум.
Если вы примените это к вашему примеру IParser
интерфейса, вы можете ясно увидеть, что абстракция полностью находится на «бессмысленной» территории. Или есть что - то конкретное о конкретной реализации синтаксического анализатора (например JsonParser
, XmlParser
...), или вы должны просто использовать класс. Нет такой вещи, как «реализация по умолчанию» (хотя в некоторых средах это действительно имеет смысл - особенно COM), либо есть конкретная реализация, либо вы хотите использовать абстрактный класс или методы расширения для «значений по умолчанию». Однако в C #, если ваша кодовая база уже не опускает I
префикс -pre, сохраните его. Просто запоминайте каждый раз, когда вы видите подобный код class Something: ISomething
- это означает, что кто-то не очень хорошо следит за YAGNI и создает разумные абстракции.
[1] - Технически, это специально не упоминается в статье Лискова, но это одна из основ оригинальной статьи ООП, и в моем чтении Лискова она не оспаривала это. В менее строгой интерпретации (используемой большинством языков ООП) это означает, что любой код, использующий открытый тип, предназначенный для подстановки (т. Е. Не финальный / запечатанный), должен работать с любой соответствующей реализацией этого типа.