Когда я должен использовать интерфейс в Java? [закрытый]

49
задан TylerH 6 February 2017 в 11:15
поделиться

11 ответов

Использовать интерфейсы для определения контракта прикладного программирования (схемы, интерфейса), который «Сторонние» поставщики должны полностью соблюдать и внедрять. Таким образом, конечные пользователи могут просто кодировать в соответствии с контрактом API и легко переключаться на конкретную реализацию «под капотами» без изменения кода.

JDBC API - отличный пример. Он существует практически только из интерфейсов. Конкретные реализации представлены как «драйверы JDBC». Это позволяет вам писать весь код JDBC независимо от поставщика базы данных (БД). Вы можете просто изменить драйвер JDBC, не изменяя ни одной строки кода Java (кроме любого жестко запрограммированного кода SQL, специфичного для БД), когда захотите сменить поставщика БД.

Другой пример - Java EE API , он также содержит довольно много интерфейсов и абстрактных классов. Конкретные реализации представлены как «серверы приложений Java EE», «контейнеры сервлетов» и т. Д., Такие как Sun Glassfish, Apache Tomcat и т. Д. Это позволяет вам развернуть веб-приложение (WAR) на любом веб-сервере Java, который вам нравится.

35
ответ дан 7 November 2019 в 11:22
поделиться

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

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

Затем у нас есть AbstractList : абстрактный класс, реализующий List , который обеспечивает реализацию почти всего, что необходимо полноценному List - для создания собственного List , все, что вам нужно сделать, это расширить AbstractList и заполнить несколько методов. Это яркий пример того, когда абстрактный класс является хорошим выбором - когда вы хотите предоставить почти полную реализацию чего-то, в котором не хватает лишь нескольких пробелов, которые необходимо заполнить клиентским кодом.

Подсказка: Если вы создаете абстрактный класс, содержащий только абстрактные методы, вам, вероятно, следует сделать из него интерфейс.

3
ответ дан 7 November 2019 в 11:22
поделиться

Попытайтесь понять Шаблон разработки стратегии

4
ответ дан 7 November 2019 в 11:22
поделиться

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

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

Аргументы весьма убедительны: при правильном дизайне всегда можно заменить наследование классов на наследование интерфейса, и вы получите много преимуществ в отношении поддержки кода. Также легче сохранить отношения естественного типа (например, из геометрии («квадрат - прямоугольник» - материал), чем при наследовании классов.

1
ответ дан 7 November 2019 в 11:22
поделиться

Взгляните на руководство по сбору JDK по ссылке текст ссылки . Подумайте о коллекциях. Что вам приходит в голову? Он может быть заказан или не заказан, и у него могут быть дубликаты или нет.

Итак, Collection - это интерфейс с подчиненными интерфейсами List (упорядоченный) и Set (неупорядоченный). Теперь есть много вопросов со списками, должен ли он быть синхронизирован / или нет, должен ли это быть связанный список или нет и т. Д. Каждое «поведение» будет иметь свои собственные интерфейсы / абстрактные классы.

Абстрактные классы необходимы, когда вы хотите указать «какое-то» поведение в коллекциях. Например, все коллекции (наборы / списки и т. Д.) Могут иметь представление "toString", которое просто перебирает элементы (упорядоченные / нет) и преобразовывает их в строку. Такое поведение может присутствовать в "AbstractCollection" и т. Д.

Если вы следуете иерархии коллекций JDK, это отличное место для изучения интерфейсов и абстрактных классов :)

4
ответ дан 7 November 2019 в 11:22
поделиться

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

public interface Xmlizable
{
    public String toXML();
}

тогда вы можете просто передать "Xmlizable" интерфейсы в методы, которые заботятся только об этом одном интерфейсе.

3
ответ дан 7 November 2019 в 11:22
поделиться

Хорошим местом для изучения была бы структура коллекций.

java.util.List //interface

java.util.ArrayList //Concrete class
java.util.LinkedList //Concrete class

Итак, вы можете написать такой код:

List l = new ArrayList();

l.add(..)
//do something else.

Если в будущем вы захотите изменить реализацию, скажем, LinkedList или у вас есть AwesomeList, который реализует интерфейс List , все, что у вас есть нужно изменить самую первую строку на:

List l = new MyAwesomeList();
or
List l = new LinkedList();

Остальная часть кода будет выполнена.

67
ответ дан 7 November 2019 в 11:22
поделиться

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

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

В качестве примера предположим, что вы хотите прочитать произвольные данные и распечатать их, например:

SomeReader someReader = new SomeReader();
String data = someReader.readLine();
System.out.println(data);

Здесь ничего особенного, не так ли? Но хотя этот пример прост, он уже привязан к классу SomeReader , что означает, что все изменения, которые вы вносите в этот класс, должны быть распространены на класс, в котором вы используете class - особенно если вы реорганизуете некоторые внутренние части! Вместо этого вы хотите сделать это

IMyReader reader = new SomeReader();
System.out.println(reader.readLine());

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

Итак, чтобы действительно нарушить пропорции этого примера, вот как ваш код может в конечном итоге выглядеть

public class RowPrinter {

    private final IMyReader reader;

    public RowPrinter(IMyReader reader) {
        this.reader = reader;
    }

    public void print() {
        IMyReader reader = getReader();
        System.out.println(reader.readLine());
    }

    protected IMyReader getReader() {
        return reader;
    }
}

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

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

3
ответ дан 7 November 2019 в 11:22
поделиться

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

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

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

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

Одно важное правило, которое следует учитывать для безопасного выполнения этого действия, - это Принцип замены Лискова [ Дядя Боб , Википедия ]. В то время как компилятор на таком языке, как Java, будет следить за тем, чтобы синтаксически все было в порядке (правильное количество параметров, типов, ...), LSP имеет дело с семантикой . Короче говоря, LSP говорит, что каждая реализация интерфейса должна (также) вести себя правильно, чтобы быть действительно заменяемой, как описано выше.

14
ответ дан 7 November 2019 в 11:22
поделиться

Основной принцип ООП - скрытие информации : скрыть детали реализации и показать вызывающему только описание основных услуг.

Java должна создавать для этой цели: интерфейсы и абстрактные классы. Вы можете определить интерфейс и написать свой код, который зависит только от интерфейса, вызвав определенные в нем «доступные методы».

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

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

2
ответ дан 7 November 2019 в 11:22
поделиться

Интерфейсы в удаленной связи:

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

Боковое примечание:

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

2
ответ дан 7 November 2019 в 11:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: