Интерфейсы Java / Соглашение об именах реализации
Как вы называете различные классы / интерфейсы, которые вы создаете? Иногда у меня нет информации о реализации, которую можно добавить к интерфейсу с именем реализации FileHandler
и класс SqlFileHandler
,
Когда это происходит, я обычно называю интерфейс "нормальным" именем, например Truck
и назовите фактический класс TruckClass
,
Как вы называете интерфейсы и классы в этом отношении?
9 ответов
Назовите свой Interface
что это. Truck
, Не ITruck
потому что это не ITruck
это Truck
,
Interface
в Java это тип. Тогда у вас есть DumpTruck
, TransferTruck
, WreckerTruck
, CementTruck
и т. д. implement Truck
,
Когда вы используете Interface
вместо подкласса вы просто бросаете его Truck
, Как в List<Truck>
, Ввод I
впереди - просто тавтология нотации в венгерском стиле, которая добавляет к вашему коду только что-то большее, чем просто набирать текст.
Все современные Java IDE отмечают Интерфейсы и Реализации и что не без этой глупой нотации. Не называй это TruckClass
это тавтология так же плохо, как IInterface
тавтология.
Если это реализация, то это класс. Единственным реальным исключением из этого правила, и всегда есть исключения, может быть что-то вроде AbstractTruck
, Поскольку только подклассы когда-либо увидят это, и вы никогда не должны бросать Abstract
класс он добавляет некоторую информацию о том, что класс является абстрактным и как его следует использовать. Вы все еще можете придумать лучшее имя, чем AbstractTruck
и использовать BaseTruck
или же DefaultTruck
вместо того, чтобы abstract
находится в определении. Но с тех пор Abstract
классы никогда не должны быть частью какого-либо общедоступного интерфейса. Я считаю, что это приемлемое исключение из правила. Создание конструкторов protected
проходит долгий путь преодоления этого разрыва.
И Impl
суффикс - это просто больше шума. Больше тавтологии. Все, что не является интерфейсом, является реализацией, даже абстрактные классы, которые являются частичными реализациями. Вы собираетесь поставить это глупо Impl
суффикс на каждом имени каждого класса?
Interface
это контракт о том, что должны поддерживать публичные методы и свойства, это также информация о типе. Все, что реализует Truck
это тип Truck
,
Посмотрите на саму стандартную библиотеку Java. Ты видишь IList
, ArrayListImpl
, LinkedListImpl
? Нет видишь List
а также ArrayList
, а также LinkedList
, Вот хорошая статья об этом точном вопросе. Любые из этих глупых соглашений о присвоении имен префиксам / суффиксам также нарушают принцип DRY.
Кроме того, если вы добавите DTO
, JDO
, BEAN
или другие глупые повторяющиеся суффиксы к объектам, тогда они, вероятно, принадлежат пакету вместо всех этих суффиксов. Правильно упакованные пространства имен являются самодокументируемыми и сокращают всю бесполезную избыточную информацию в этих действительно плохо продуманных запатентованных схемах именования, которые большинство мест даже внутренне не придерживаются согласованным образом.
Если все, что вы можете придумать, чтобы сделать ваш Class
имя уникальное - это суффикс Impl
то вам нужно переосмыслить Interface
совсем. Поэтому, когда у вас есть ситуация, когда у вас есть Interface
и один Implementation
это не однозначно специализируется от Interface
вам, вероятно, не нужно Interface
,
Я видел ответы, которые показывают, что если у вас есть только одна реализация, то вам не нужен интерфейс. Это противоречит принципу внедрения зависимости / инверсии зависимости (не звоните нам, мы вам позвоним!).
Так что да, есть ситуации, в которых вы хотите упростить свой код и сделать его легко тестируемым, полагаясь на внедренные реализации интерфейса (которые также могут быть проксированы - ваш код не знает!). Даже если у вас есть только две реализации - одна макетная для тестирования, и другая, внедряемая в реальный производственный код - это не делает лишним интерфейс. Хорошо документированный интерфейс устанавливает контракт, который также может поддерживаться строгой фиктивной реализацией для тестирования.
на самом деле, вы можете создавать тесты, в которых макеты реализуют наиболее строгий интерфейсный контракт (выбрасывая исключения для аргументов, которые не должны быть нулевыми и т. д.) и отлавливать ошибки при тестировании, используя более эффективную реализацию в рабочем коде (не проверяя аргументы, которые должны не быть нулевым за то, что оно является нулевым, так как макет выдавал исключения в ваших тестах, и вы знаете, что аргументы не являются нулевыми из-за исправления кода после этих тестов, например).
Внедрение зависимостей /IOC может быть трудно понять новичку, но как только вы поймете его потенциал, вам захочется использовать его повсеместно, и вы обнаружите, что создаете интерфейсы все время - даже если будет только один (фактическое производство) реализация.
Для этой одной реализации (вы можете сделать вывод, и вы были бы правы, я считаю, что макеты для тестирования должны называться Mock(InterfaceName)), я предпочитаю имя Default(InterfaceName). Если появляется более конкретная реализация, она может быть названа соответствующим образом. Это также позволяет избежать суффикса Impl, который мне особенно не нравится (если это не абстрактный класс, OF COURSE, то это "impl"!).
Я также предпочитаю "Base (InterfaceName)", а не "Abstract (InterfaceName)", потому что есть некоторые ситуации, в которых вы хотите, чтобы ваш базовый класс стал инстанцируемым позже, но теперь вы застряли с именем "Abstract (InterfaceName)", и это вынуждает вас переименовывать класс, возможно, вызывая небольшую путаницу - но если это всегда было Base (InterfaceName), удаление абстрактного модификатора не изменит то, чем был класс.
Имя интерфейса должно описывать абстрактную концепцию, которую представляет интерфейс. Любой класс реализации должен иметь какие-то особые черты, которые можно использовать, чтобы дать ему более конкретное имя.
Если существует только один класс реализации, и вы не можете придумать ничего, что делает его конкретным (подразумевается, если вы хотите назвать его -Impl
), то похоже, что нет никакого оправдания иметь интерфейс вообще.
Я склонен следовать псевдо-соглашениям, установленным Java Core/Sun, например, в классах Collections:
List
- интерфейс для "концептуального" объектаArrayList
- конкретная реализация интерфейсаLinkedList
- конкретная реализация интерфейсаAbstractList
- абстрактная "частичная" реализация для поддержки пользовательских реализаций
Я делал то же самое, моделируя классы событий после парадигмы AWT Event/Listener/Adapter.
Стандартное соглашение C#, которое работает достаточно хорошо и в Java, заключается в добавлении префиксов ко всем интерфейсам I
- так интерфейс вашего обработчика файлов будет IFileHandler
и ваш интерфейс грузовика будет ITruck
, Он последовательный и позволяет легко отличать интерфейсы от классов.
Мне нравятся имена интерфейсов, которые указывают, какой контракт описывает интерфейс, например, "Comparable" или "Serializable". Существительные типа "Грузовик" на самом деле не описывают самосвал - каковы способности грузовика?
Что касается соглашений: я работал над проектами, где каждый интерфейс начинается с "я"; хотя это несколько чуждо соглашениям Java, это делает поиск интерфейсов очень простым. Кроме того, суффикс "Impl" является разумным именем по умолчанию.
Некоторым это не нравится, и это скорее соглашение.NET, чем Java, но вы можете назвать свои интерфейсы с префиксом I, например:
IProductRepository - interface
ProductRepository, SqlProductRepository, etc. - implementations
Люди, выступающие против этого соглашения об именах, могут возразить, что вас не должно волновать, работаете ли вы с интерфейсом или объектом в вашем коде, но мне легче читать и понимать на лету.
Я бы не назвал класс реализации суффиксом "Класс". Это может привести к путанице, потому что вы на самом деле можете работать с объектами "класса" (т.е. Type) в вашем коде, но в вашем случае вы не работаете с объектом класса, вы просто работаете с простым старым объектом,
Я использую оба соглашения:
Если интерфейс является конкретным экземпляром хорошо известного шаблона (например, Service, DAO), тогда ему может не понадобиться "I" (например, UserService, AuditService, UserDao), все нормально работают без "I", потому что после исправления определяет мета паттерн.
Но если у вас есть что-то одноразовое или двухразовое (обычно для шаблона обратного вызова), то это помогает отличить его от класса (например, IAsynchCallbackHandler, IUpdateListener, IComputeDrone). Это интерфейсы специального назначения, предназначенные для внутреннего использования, иногда IInterface обращает внимание на тот факт, что операнд на самом деле является интерфейсом, поэтому на первый взгляд он становится понятным.
В других случаях вы можете использовать I, чтобы избежать столкновения с другими общеизвестными конкретными классами (ISubject, IPrincipal vs Subject или Principal).
TruckClass
Похоже, это был класс Truck
Я думаю, что рекомендуемое решение - добавить Impl
суффикс. На мой взгляд, лучшее решение - это содержать в имени реализации некоторую информацию о том, что происходит в этой конкретной реализации (как у нас с List
интерфейс и реализации: ArrayList
или же LinkedList
), но иногда вы имеете только одну реализацию и должны иметь интерфейс из-за удаленного использования (например), затем (как упоминалось в начале) Impl
это решение.