Интерфейсы, имеющие только одну реализацию - PullRequest
6 голосов
/ 30 июля 2011

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

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

Это просто цена, которую вы платите за правильную абстракцию, или есть лучший подход, о котором я не подумал? Должны ли все классы реализовывать интерфейс, даже если в интерфейсе есть только геттеры / сеттеры?

Ответы [ 9 ]

10 голосов
/ 30 июля 2011

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

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

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

5 голосов
/ 30 июля 2011

Некоторые общие моменты, по которым я иду:

  • Образцы в вашем тесте являются альтернативными реализациями. Поэтому, даже если у вас есть только одна реализация в коде продукта, в базе кода будет более одной реализации.
  • Предпочитают инжекцию конструктора геттерам и сеттерам. Это также сделает неизменные объекты более естественными.
  • Интерфейсы описывают контракт части поведения и делают это таким образом, чтобы пользователям интерфейса не нужно было знать о деталях реализации.
  • Не создавайте интерфейсы для вещей, которые не связаны с этими вещами: абстрагирование деталей реализации; несколько альтернативных реализаций; mockability; поведение.
  • Предпочтение отдается конструкциям, в которых много мелких составных частей, а не конструкциям, в которых мало крупных взаимосвязанных деталей.
  • Если что-то сложно проверить или смоделировать, попробуйте разбить его на более мелкие части с более простыми API-интерфейсами, а затем составьте их.
  • Дайте вещи точные имена и обратите внимание на дублирование не только в коде, но также в структуре и именах.
  • Избегайте наследования реализации - вместо этого предпочтите композицию. Наследование интерфейса сложнее использовать, и оно часто выпадает естественным образом, когда это необходимо.

Эти пункты, мы надеемся, помогут вам сделать более простые и более проверяемые и поддерживаемые проекты. По крайней мере, после небольшого опыта, пытаясь следовать им.

3 голосов
/ 30 июля 2011

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

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

В общем, я не уверен, что немедленное предоставление интерфейса для всего представляет собой правильную абстракцию. Скорее, ожидание стабилизации общедоступного интерфейса класса, а затем рассмотрение правильной абстракции (т. Е. не простым нажатием кнопки "extract interface..." в IDE) намного лучше с точки зрения дизайна. Кроме того, класс может абстрагироваться в несколько не связанных между собой интерфейсов, а не в один интерфейс.

3 голосов
/ 30 июля 2011

Если он многословен и утомителен, это плохой стиль, независимо от теоретических преимуществ.

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

Конечно, все споры по этим вопросам - просто мнение.Фактов не найдено.

1 голос
/ 30 июля 2011

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

interface IRunner {
      void run();
      int doOtherThing();
}

abstract class Thing implements IRunner {

    //this is class-specific
    abstract void run();
    //this is common to all 'Things'
    int doOtherThing() { return 0; }

    //as are these properties
    public int getProp() {...}
    public void setProp(int val) {...}

}

public class Goo extends Thing {
      public void run() {
         int i = getProp() + doOtherThing();
         makeMagicGoo(i);
      }

}
0 голосов
/ 04 августа 2017

Я бы порекомендовал это:

  • Нет интерфейсов для простых классов в стиле Value или DTO, особенно в неизменяемых
  • Интерфейсы не требуются для локальных классов пакетов с очень ограниченными зависимостями от других неинтерфейсных классов в их конструкторах. Неприватные методы класса являются его интерфейсом по определению.
  • Используйте интерфейсы, если возможно извлечь подчиненные интерфейсы, в соответствии с принципом разделения интерфейсов
  • Я все еще использую интерфейсы между пакетами и модулями, т.е. я предпочитаю, чтобы мой пакет предоставлял только открытые интерфейсы, а не публичные классы
  • Несмотря на мощь Mockito и подобных библиотек, mock реального класса все равно будет вызывать его конструктор. Поэтому, если конструктор использует другие фактические классы или имеет некоторую нетривиальную логику генерирования исключений, я бы использовал интерфейс, чтобы избежать неожиданностей при написании юнит-тестов для других классов

Пара полезных ссылок по теме:
https://martinfowler.com/bliki/InterfaceImplementationPair.html
https://rrees.me/2009/01/31/programming-to-interfaces-anti-pattern/

0 голосов
/ 13 декабря 2012

пожалуйста, обратитесь к ответу, опубликованному здесь, который фокусируется на роли интерфейса определения контракта https://softwareengineering.stackexchange.com/a/179265/74889

0 голосов
/ 30 июля 2011

Есть один ключевой вариант использования, когда создание интерфейса для объекта жизненно важно, даже когда вы пишете только одну его реализацию: где вы хотите иметь возможность генерировать JDK-прокси для экземпляров класса (это полезноесли вы делаете Spring AOP, например).Можно использовать что-то вроде cglib для создания прокси для произвольных вещей, но это намного сложнее;Для сравнения прокси-серверы JDK просты.

Я также немного обеспокоен тем, что ваша IDE не помогает вам так, как следовало бы.Я обнаружил, что с моим (Eclipse) я могу создавать методы в интерфейсе, если я просто отмечу их @Override в реализации и выберу правильный вариант авто-исправления.(Это не значит, что он выберет правильную интерпретацию метода, но это ваша работа.)

0 голосов
/ 30 июля 2011

Если наличие дополнительных интерфейсов оскорбляет вас, вы можете создать фиктивные объекты (вручную) как подклассы классов, которые вы осмеиваете.

Если класс просто состоит из (простых) геттеров и сеттеров, вряд ли стоит над ним издеваться. Просто используйте настоящую вещь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...