Spring и интерфейсы

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

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Перевод примеров из приведенной выше ссылки в 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 без каких-либо изменений в его коде.

И теперь с Spring:

<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" />

Ответ 3

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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