Должны ли имена интерфейсов начинаться с префикса «I»?

73

Я читал « Чистый код » Роберта Мартина, чтобы, надеюсь, стать лучшим программистом. Хотя до сих пор ни один из них не был действительно новаторским, это заставило меня по-другому думать о том, как я проектирую приложения и пишу код.

Есть одна часть книги, с которой я не только не согласен, но и не имеет смысла, особенно в отношении соглашений об именах интерфейсов. Вот текст, взятый прямо из книги. Я обдумал этот аспект, который меня смущает, и хотел бы получить разъяснения.

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

Возможно, это потому, что я всего лишь студент, или, может быть, потому что я никогда не занимался профессиональным или командным программированием, но я бы хотел, чтобы пользователь знал, что это интерфейс. Существует большая разница между реализацией интерфейса и расширением класса.

Итак, мой вопрос сводится к следующему: «Почему мы должны скрывать тот факт, что некоторая часть кода ожидает интерфейс?»

редактировать

В ответ на ответ:

Если ваш тип - это интерфейс или класс - это ваше дело, а не бизнес того, кто использует ваш код. Так что вы не должны пропускать детали своего кода в этот сторонний код.

Почему я не должен «утекать» детали того, является ли данный тип интерфейсом или классом для стороннего кода? Разве не важно, чтобы сторонний разработчик, использующий мой код, знал, будут ли они реализовывать интерфейс или расширять класс? Разве различия не так важны, как я их представляю?

Чарльз Спрейберри
источник
3
Я согласен с вашей точкой зрения. Есть момент, когда скрытие слишком большого количества информации не очень полезно. Однако, даже если вы будете следовать этому руководству, вы все равно сможете определить тип с помощью IDE или дополнения.
NoChance
3
Этот вопрос по сути известен как вопрос «венгерской нотации», вы должны найти множество аргументов и причину, по которой большинство разработчиков не-MS отказались от него под этим ключевым словом. Венгерская нотация была в основном распространена для переменных, но по сути она одинакова для типов.
thiton
2
Предыдущее редактирование заголовка было ужасным. Этот вопрос не о венгерской нотации в целом просто потому, что он упоминает соглашение, которое может быть связано с ним. Относительные достоинства HN здесь совершенно не имеют значения; вопрос был конкретно об интерфейсах и классах и о том, являются ли семантические различия достаточно важными / интересными, чтобы оправдать соглашение об именовании в особых случаях.
Aaronaught
Re to know whether they will be implementing an interface or extending a class: да, но большинство пользователей вашего кода будут называть его, а не реализовывать или расширять его, и им действительно было все равно, что это.
david.pfx
Что бы это ни стоило, я предпочитаю префикс «я». Я также использую префикс «Abstract» в абстрактных классах по той же причине. Это не имеет значения для потребителей класса / интерфейса, но может иметь большое значение для тех, кто должен предоставить экземпляры этого, а также делает его намного проще для других разработчиков, которые читают ваш код. Это означает, что они могут сразу увидеть, с чем имеют дело, вместо того, чтобы обращаться за дополнительной информацией к IDE в каждом конкретном случае. Я только начал использовать Angular, и меня действительно раздражает, что они не следуют этому соглашению!
Дэн Кинг

Ответы:

67

Если вы перестанете думать об этом, вы увидите, что интерфейс действительно не сильно отличается от абстрактного класса:

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

На самом деле, наиболее важные различия между классами и интерфейсами:

  • Интерфейсы не могут иметь личных данных;
  • Члены интерфейса не могут иметь модификаторы доступа (все члены являются «общедоступными»);
  • Класс может реализовывать несколько интерфейсов (в отличие от возможности наследовать только от одного базового класса).

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

Тем не менее, есть определенные угловые случаи, о которых следует знать. В частности, если вы используете рефлексию, перехват, динамические прокси / миксины, переплетение байт-кода, генерацию кода или все, что связано с перепиской непосредственно с системой типизации среды или с самим кодом, - это очень полезно, а иногда необходимо знать об этом сразу же. летите ли вы с интерфейсом или классом. Вы явно не хотите, чтобы ваш код таинственным образом терпел неудачу, потому что вы пытались добавить класс, а не интерфейс, в качестве миксина.

Однако для типичного, ванильного, заурядного кода бизнес-логики различия между абстрактными классами и интерфейсами не нужно рекламировать, потому что они никогда не вступят в игру.

Все это, как говорится, в Iлюбом случае я склоняюсь к префиксам моих интерфейсов C #, потому что это соглашение .NET, используемое и поддерживаемое Microsoft. И когда я объясняю соглашения о кодировании новому разработчику, гораздо проще использовать правила Microsoft, чем объяснять, почему у нас есть свои «специальные» правила.

Aaronaught
источник
Спасибо за эту поломку. +1 за «значимые различия между классами и интерфейсами ...»
Чарльз Спрейберри
24
+1 за все это, как говорится, я все равно склоняюсь к тому, чтобы мои интерфейсы C # добавлялись к I, потому что это соглашение .NET, используемое и поддерживаемое Microsoft ". Это достаточная причина для меня в C #. Следуя этому общему стандарту, более вероятно, что другие программисты .NET идентифицируют интерфейс.
Robotsushi
4
Как кто-то, кто пишет и на Java, и на C #, утверждение «Все это сказано, я все равно склоняюсь к тому, чтобы мои интерфейсы C # добавлялись к I, потому что это соглашение .NET, используемое и поддерживаемое Microsoft», нельзя недооценивать - это одна из вещей, которые помогает мне запомнить, на каком языке я работаю,
Aidos
3
Другое отличие в .NET (но не в Java) заключается в том, что многие языки .NET не позволяют интерфейсам содержать статические методы, поэтому взлом Iинтерфейса может дать хорошее имя классу для хранения статических методов, связанных с интерфейсом (например, Enumerable<T>.Emptyили Comparer<T>.Default).
суперкат
У меня есть одна проблема. В 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, хотя я лично предпочел бы, чтобы на моих интерфейсах не было Is, поскольку несовместимость с библиотеками BCL и сторонних разработчиков, используемыми в проектах .Net, не является вариантом imho.
JK.
13

Книга полна хороших вещей, но я все равно добавил бы «I» к имени интерфейса.

Представьте, что у вас есть public interface ILogреализация по умолчанию public class Log.

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

Часто между теорией и практикой существует тонкая грань. В теории это действительно хорошая идея, но на практике это не так хорошо.

CodeART
источник
Это также упомянуто в документации
levininja
30
«Представьте, что у вас есть открытый интерфейс ILog с общедоступным классом Log по умолчанию». - Тогда у вас плохое имя для вашей реализации по умолчанию. Что делает Log? Вход в консоль? В системный журнал? В никуда? Те , следует назвать ConsoleLog, SyslogLogи NullLog, соответственно. Logне является хорошим именем для конкретного класса, потому что это не конкретное имя.
Себастьян Редл
7
Лично это именно то, почему я ненавижу видеть ITheClassName, вы просто создаете интерфейс без причины, это просто шум поверх TheClassName. Если у вашего интерфейса есть цель, то должно быть возможно дать ему лучшее имя, чем ITheClassName
Ричард Тингл
Имя класса используется один раз (для создания экземпляра), имя интерфейса используется везде. Добавление буквы в интерфейс для сохранения буквы на имени класса является ложной экономикой (из символов).
Сергут
@RichardTingle Но я думаю, что общее использование там просто так, что MyClassимеет насмешливый вариант, верно? То есть мы часто используем интерфейсы, чтобы сделать публичные методы действительно общедоступными, что позволяет проводить тестирование. (Не говоря уже о том, хорошо это или плохо, но понимание того, что мотивация для интерфейсов часто состоит в том, чтобы просто сделать классы, которые легко поддаются моделированию, объясняет сопоставления « один MyClassк IMyClass
одному»
8

Ну, это не о реализации интерфейса или расширении класса. В этих случаях вы все равно знаете, что делаете.

Однако, когда сторонний код (другой модуль приложения для примера) манипулирует вашими данными, этот код не должен заботиться о том, представляете ли вы интерфейс или класс.

В этом весь смысл абстракции. Вы представляете этому стороннему коду объект данного типа. Данный тип имеет некоторую функцию-член, которую вы можете вызвать. Достаточно.

Если ваш тип - это интерфейс или класс - это ваше дело, а не бизнес того, кто использует ваш код. Таким образом, вы не должны передавать информацию о своем коде этому стороннему коду.

Кстати, интерфейсы и классы являются ссылочными типами в конце. И это то, что имеет значение. Так что это то, что ваше соглашение об именах должно подчеркнуть.

deadalnix
источник
@ Mat> Да, это была моя ошибка, и я отредактировал ее.
Deadalnix
5

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

Однако, по моему мнению, в C ++ ситуация немного отличается, так как нужно погрузиться в заголовочный файл и посмотреть, есть ли у класса какие-либо виртуальные методы, чтобы выяснить, является ли он абстрактным классом. В этом случае я ясно вижу аргумент для написания class AbstractMyClass.

Поэтому мой ответ будет таким: это зависит от языка программирования.

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

Ela782
источник
Вы уверены, что это отвечает на вопрос? Возможно, вы захотите прочитать другие ответы и уточнить некоторые из ваших пунктов.
david.pfx
C ++ определенно имеет интерфейсы, даже если для него нет ключевого слова. Что вы называете классом, где каждая функция-член является чисто виртуальной, и в ней нет переменных-членов?
@ david.pfx: я думаю, что точно отвечаю на вопрос «Должны ли имена интерфейсов начинаться с префикса« I »?»: это зависит от языка программирования. Если вы думаете, что мои разработки недостаточно ясны, я с удовольствием их улучшу - какие части вам не понятны?
Ela782
2
@JohnGaughan: Конечно, у него есть интерфейсы! Но все дело в том, что речь идет о ключевом слове. В C ++ такого нет, поэтому IDE / пользователю не видно, имеет ли он дело с интерфейсом или нет. В Java, однако, это сразу видно.
Ela782
1
Прочитайте ответ с самым высоким рейтингом и сравните свой. Вы новичок в SO, и это возможность узнать, какие ответы привлекают голоса. Как он есть, ваш не будет. С большей работой, большим количеством объяснений, большей ценностью, это возможно. Повесить там!
david.pfx
4

Следуйте идиомам языка, который вы используете.

Каждый язык имеет свои собственные идиомы и правила кодирования. Например, в C # идиоматично префиксировать интерфейсы с I. В Go это не так.

Пит
источник
+1 Следуйте идиомам языка, который вы используете. Очевидно, автор книги - разработчик на Java. .NET / C #: ILog Java: Log Но недостаток: когда мне нравится, есть класс Log, который реализует интерфейс ILog .... MyLog, LogDefault, LogBase, ...? Ужасно, не правда ли? Более уродливым является: LogImpl ....: D
hfrmobile
0

В моем (Java) коде я склонен иметь это соглашение в API, которые я представляю вызывающим:

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

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

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

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

Donal Fellows
источник
Я также никогда не ставлю Iпрефиксы на интерфейсах. Из Конечно , это интерфейс! Это в API (или SPI)!
Донал Феллоуз