Я читал « Чистый код » Роберта Мартина, чтобы, надеюсь, стать лучшим программистом. Хотя до сих пор ни один из них не был действительно новаторским, это заставило меня по-другому думать о том, как я проектирую приложения и пишу код.
Есть одна часть книги, с которой я не только не согласен, но и не имеет смысла, особенно в отношении соглашений об именах интерфейсов. Вот текст, взятый прямо из книги. Я обдумал этот аспект, который меня смущает, и хотел бы получить разъяснения.
Я предпочитаю оставлять интерфейсы без украшений. Предыдущее «я», столь распространенное в сегодняшних устаревших пачках, в лучшем случае отвлекает, а в худшем - слишком много информации. Я не хочу, чтобы мои пользователи знали, что я передаю им интерфейс .
Возможно, это потому, что я всего лишь студент, или, может быть, потому что я никогда не занимался профессиональным или командным программированием, но я бы хотел, чтобы пользователь знал, что это интерфейс. Существует большая разница между реализацией интерфейса и расширением класса.
Итак, мой вопрос сводится к следующему: «Почему мы должны скрывать тот факт, что некоторая часть кода ожидает интерфейс?»
редактировать
В ответ на ответ:
Если ваш тип - это интерфейс или класс - это ваше дело, а не бизнес того, кто использует ваш код. Так что вы не должны пропускать детали своего кода в этот сторонний код.
Почему я не должен «утекать» детали того, является ли данный тип интерфейсом или классом для стороннего кода? Разве не важно, чтобы сторонний разработчик, использующий мой код, знал, будут ли они реализовывать интерфейс или расширять класс? Разве различия не так важны, как я их представляю?
источник
to know whether they will be implementing an interface or extending a class
: да, но большинство пользователей вашего кода будут называть его, а не реализовывать или расширять его, и им действительно было все равно, что это.Ответы:
Если вы перестанете думать об этом, вы увидите, что интерфейс действительно не сильно отличается от абстрактного класса:
На самом деле, наиболее важные различия между классами и интерфейсами:
Поскольку единственные особенно значимые различия между классами и интерфейсами вращаются вокруг (а) личных данных и (б) иерархии типов - ни одна из которых не имеет ни малейшего значения для вызывающей стороны - обычно нет необходимости знать, является ли тип интерфейсом или класс. Вам, конечно, не нужна визуальная индикация.
Тем не менее, есть определенные угловые случаи, о которых следует знать. В частности, если вы используете рефлексию, перехват, динамические прокси / миксины, переплетение байт-кода, генерацию кода или все, что связано с перепиской непосредственно с системой типизации среды или с самим кодом, - это очень полезно, а иногда необходимо знать об этом сразу же. летите ли вы с интерфейсом или классом. Вы явно не хотите, чтобы ваш код таинственным образом терпел неудачу, потому что вы пытались добавить класс, а не интерфейс, в качестве миксина.
Однако для типичного, ванильного, заурядного кода бизнес-логики различия между абстрактными классами и интерфейсами не нужно рекламировать, потому что они никогда не вступят в игру.
Все это, как говорится, в
I
любом случае я склоняюсь к префиксам моих интерфейсов C #, потому что это соглашение .NET, используемое и поддерживаемое Microsoft. И когда я объясняю соглашения о кодировании новому разработчику, гораздо проще использовать правила Microsoft, чем объяснять, почему у нас есть свои «специальные» правила.источник
I
интерфейса может дать хорошее имя классу для хранения статических методов, связанных с интерфейсом (например,Enumerable<T>.Empty
илиComparer<T>.Default
).class Foo
уже расширяетсяclass Bar
, это не сразу очевидноclass Bar
, расширяется или реализуется. Так что теперь вам нужно пойти и заглянуть вclass Bar
заголовок, чтобы решить, можно ли изменить егоclass Foo
для расширенияclass Baz
. Более того, префикс I дает очевидную визуальную подсказку, нарушается «единственный базовый класс» или нет - поэтому вы получаете проверку работоспособности каждый раз, когда открываете заголовок.Во многих отношениях последовательность важнее, чем соглашение. Пока вы последовательны в своих схемах именования, с ними не составит труда работать. Префикс взаимодействует с I, если вам нравится, или просто оставляете имя без украшения, для меня это не имеет значения, пока вы выбираете стиль и придерживаетесь его!
источник
Книга полна хороших вещей, но я все равно добавил бы «I» к имени интерфейса.
Представьте, что у вас есть
public interface ILog
реализация по умолчаниюpublic class Log
.Теперь, если вы решите это сделать
public interface Log
, вам внезапно придется изменить класс Log наpublic class LogDefault
что-то в этих строках. Вы перешли от одного дополнительного персонажа к семи - конечно, это расточительно.Часто между теорией и практикой существует тонкая грань. В теории это действительно хорошая идея, но на практике это не так хорошо.
источник
Log
? Вход в консоль? В системный журнал? В никуда? Те , следует назватьConsoleLog
,SyslogLog
иNullLog
, соответственно.Log
не является хорошим именем для конкретного класса, потому что это не конкретное имя.MyClass
имеет насмешливый вариант, верно? То есть мы часто используем интерфейсы, чтобы сделать публичные методы действительно общедоступными, что позволяет проводить тестирование. (Не говоря уже о том, хорошо это или плохо, но понимание того, что мотивация для интерфейсов часто состоит в том, чтобы просто сделать классы, которые легко поддаются моделированию, объясняет сопоставления « одинMyClass
кIMyClass
Ну, это не о реализации интерфейса или расширении класса. В этих случаях вы все равно знаете, что делаете.
Однако, когда сторонний код (другой модуль приложения для примера) манипулирует вашими данными, этот код не должен заботиться о том, представляете ли вы интерфейс или класс.
В этом весь смысл абстракции. Вы представляете этому стороннему коду объект данного типа. Данный тип имеет некоторую функцию-член, которую вы можете вызвать. Достаточно.
Если ваш тип - это интерфейс или класс - это ваше дело, а не бизнес того, кто использует ваш код. Таким образом, вы не должны передавать информацию о своем коде этому стороннему коду.
Кстати, интерфейсы и классы являются ссылочными типами в конце. И это то, что имеет значение. Так что это то, что ваше соглашение об именах должно подчеркнуть.
источник
Большинство ответов, похоже, предполагают, что язык программирования - это либо Java, либо C #, где существует концепция (или ключевое слово) для интерфейсов / абстрактных классов. В этих случаях в приличной среде IDE сразу видно, с каким типом класса один имеет дело, и запись
public interface IMyClass
является ненужным дублированием. Это как если бы вы не писалиpublic final FinalMyClass
- представьте, что вы вводите каждое ключевое слово в название классаОднако, по моему мнению, в C ++ ситуация немного отличается, так как нужно погрузиться в заголовочный файл и посмотреть, есть ли у класса какие-либо виртуальные методы, чтобы выяснить, является ли он абстрактным классом. В этом случае я ясно вижу аргумент для написания
class AbstractMyClass
.Поэтому мой ответ будет таким: это зависит от языка программирования.
Обновленный 2016: 2 года опыта работы с C ++. Теперь я, вероятно, посчитал бы плохой практикой добавлять префиксы к именам классов
Abstract
, хотя я бы сказал, что, вероятно, существуют настолько сложные, что базы кода могут иметь смысл.источник
Следуйте идиомам языка, который вы используете.
Каждый язык имеет свои собственные идиомы и правила кодирования. Например, в C # идиоматично префиксировать интерфейсы с I. В Go это не так.
источник
В моем (Java) коде я склонен иметь это соглашение в API, которые я представляю вызывающим:
Под нефункциональными я имею в виду такие вещи, как чистые структуры данных (такие как классы, которые действуют как мосты для сериализации XML или ORM), перечисления и исключения, которые не могут быть интерфейсами (ну, вы можете это сделать для классов чистых данных , но это большая работа для очень маленькой выгоды, так как эти классы ничего не делают, кроме хранения данных).
С точки зрения соглашений об именах, интерфейсы имеют тенденцию отображаться либо на существительные субъекта (например,
FooBarWatcher
), либо на прилагательные (например,FooBarWatchable
), а классы чистых данных и перечисления отображаются на неактивные существительные (например,FooBarStatus
); IDE может направлять потребителя API без специальных соглашений об именах. Исключения следуют обычным правилам Java (FooBarException
,FooBarError
,FooBarFault
) конечно.Я также часто помещаю интерфейсы в их собственный пакет или даже в их собственную библиотеку, просто чтобы убедиться, что у меня нет соблазна нарушить мои собственные правила. (Это также помогает мне управлять сборкой, когда я извлекаю код из внешних источников, таких как документы WSDL.)
источник
I
префиксы на интерфейсах. Из Конечно , это интерфейс! Это в API (или SPI)!