пружина и интерфейсы - PullRequest
       41

пружина и интерфейсы

21 голосов
/ 02 ноября 2008

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

Ответы [ 9 ]

31 голосов
/ 02 ноября 2008

Принцип инверсии зависимостей объясняет это хорошо. В частности, рисунок 4.

A. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.

B. Абстракция не должна зависеть от деталей. Детали должны зависеть от абстракций.

Перевод примеров из ссылки выше в java:

public class Copy {
    private Keyboard keyboard = new Keyboard(); // concrete dependency
    private Printer printer = new Printer();    // concrete dependency
    public void copy() {
        for (int c = keyboard.read(); c != KeyBoard.EOF) {
            printer.print(c);
        }
    }
}

Теперь с инверсией зависимостей:

public class Copy {
     private Reader reader; // any dependency satisfying the reader interface will work
     private Writer writer; // any dependency satisfying the writer interface will work
     public void copy() {
        for (int c = reader.read(); c != Reader.EOF) {
            writer.write(c);
        }
     }
     public Copy(Reader reader, Writer writer) {
         this.reader = reader;
         this.writer = writer;
     }
}

Теперь Copy поддерживает больше, чем просто копирование с клавиатуры на принтер.

Он способен копировать с любого Reader на любой Writer, не требуя каких-либо изменений в его коде.

А теперь с весны:

<bean id="copy" class="Copy">
    <constructor-arg ref="reader" />
    <constructor-arg ref="writer" />
</bean>

<bean id="reader" class="KeyboardReader" />
<bean id="writer" class="PrinterWriter" />

или, возможно:

<bean id="reader" class="RemoteDeviceReader" />
<bean id="writer" class="DatabaseWriter" />
28 голосов
/ 02 ноября 2008

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

Но если вы хотите внедрить другой класс, который предлагает «эквивалентную» функциональность, использование интерфейса действительно помогает.

Например, говоря, что у вас есть класс, который анализирует контент веб-сайта, и вы внедряете его с помощью Spring. Если классы, в которые вы вводите его, знают, что это за класс, то для того, чтобы изменить его, вам придется изменить весь код, чтобы использовать другой конкретный класс. Но если вы создали Analyzer интерфейс, вы могли бы так же легко ввести свой исходный DefaultAnalyzer, как вы могли бы смоделировать DummyAnalyzer или даже другой, который по сути делает то же самое, например PageByPageAnalyzer или что-то еще. Чтобы использовать один из них, вам просто нужно изменить имя класса, которое вы вводите в свои конфигурационные файлы Spring, а не проходить через изменение кода в других классах.

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

10 голосов
/ 04 ноября 2008

Большинство ответов здесь представляют собой какую-то форму «Вы можете легко поменять реализации», но я думаю, что они не могут ответить, почему? часть. На это я думаю, что ответ почти окончательно проверяем. Независимо от того, используете ли вы Spring или любую другую платформу IOC, использование Dependency Injection облегчает тестирование вашего кода. В случае, скажем, Writer, а не PrinterWriter, вы можете Mock интерфейс Writer в модульном тесте, и убедитесь, что ваш код вызывает его так, как вы ожидаете. Если вы напрямую зависите от реализации класса, ваш единственный вариант - подойти к принтеру и проверить его, что не очень автоматизировано. Более того, если вы зависите от результата обращения к классу, то, что вы не можете использовать Mock, это может помешать вам получить доступ ко всем путям кода в вашем тесте, что снизит их качество (потенциально) Проще говоря, вам следует отделить Object создание графа из логики приложения. Это облегчает тестирование вашего кода.

4 голосов
/ 09 июля 2014

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

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

2 голосов
/ 02 ноября 2008

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

Вот несколько примеров:

  1. Допустим, вы пишете класс, который необходимо прочитать из ресурса (например, файла), на который можно ссылаться несколькими способами (например, в пути к классам, абсолютном пути к файлу, в виде URL-адреса и т. Д.). Вы хотите определить свойство org.springframework.core.io.Resource (interface) для вашего класса. Затем в файле конфигурации Spring вы просто выбираете фактический класс реализации (например, org.springframework.core.io.ClassPathResource, org.springframework.core.io.FileSystemResource, org.springframework.core.io.UrlResource и т. Д.). Spring в основном функционирует как чрезвычайно универсальная фабрика.

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

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

1 голос
/ 14 августа 2018

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

Такие инструменты, как Mockito, позволяют очень просто тестировать код с помощью внедрения зависимостей без добавления в интерфейсы.

1 голос
/ 19 мая 2015

Если вы не используете интерфейсы, вы рискуете вызвать ошибку автоматического подключения : Когда-нибудь Spring создает класс Proxy для Бина. Этот класс Proxy не является дочерним классом реализации службы , но он повторно реализует все свои интерфейсы. Spring попытается автоматически связать экземпляры этого Бина, однако этот класс Proxy несовместим с классом Бина. Поэтому объявление поля с помощью класса Bean может привести к исключениям «небезопасного назначения поля».

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

1 голос
/ 14 ноября 2008

легко генерировать прокси из интерфейсов.

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

0 голосов
/ 04 ноября 2008

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

Если вы используете, например, предложения поддержки Hibernate, вы можете определить интерфейс для вашего DAO, а затем реализовать его отдельно; Преимущество наличия интерфейса в том, что вы сможете настроить его с помощью перехватчиков Spring, что позволит вам упростить ваш код; вам не нужно будет писать код, содержащий HibernateExceptions и закрывать сеанс в сегменте finally, и вам также не придется определять какие-либо транзакции программно, вы просто настраиваете все это декларативно с помощью Spring.

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

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