Что значит «программировать на интерфейс»? - PullRequest
768 голосов
/ 21 декабря 2008

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

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

Это просто так, если бы вы делали:

IInterface classRef = new ObjectWhatever()

Вы могли бы использовать любой класс, который реализует IInterface? Когда вам нужно это сделать? Единственное, о чем я могу думать, это если у вас есть метод, и вы не уверены, какой объект будет передан, за исключением того, что он реализует IInterface. Я не могу думать, как часто вам нужно будет это делать.

Кроме того, как вы могли бы написать метод, который принимает объект, который реализует интерфейс? Это возможно?

Ответы [ 32 ]

2 голосов
/ 28 апреля 2010

Проще говоря ... Если я пишу новый класс Swimmer для добавления функциональности swim () и мне нужно использовать объект класса скажем Dog, и этот класс Dog реализует интерфейс Animal, который объявляет swim () [Чтобы лучше понять ... вы можете нарисовать диаграмма того, о чем я говорю]. В верхней части иерархии (Животное) это очень абстрактно, в то время как в нижней части (Собака) это очень конкретно. Я думаю о «программировании для интерфейсов» так, что, когда я пишу класс Swimmer, я хочу написать свой код в соответствии с интерфейсом, который находится настолько далеко от той иерархии, которая в данном случае является объектом Animal. Интерфейс свободен от деталей реализации и, следовательно, делает ваш код слабосвязанным. Детали реализации могут изменяться со временем, однако это не повлияет на оставшийся код, поскольку все, с чем вы взаимодействуете, - это интерфейс, а не реализация. Вам все равно, на что похожа реализация ... все, что вы знаете, это то, что будет класс, который будет реализовывать интерфейс.

2 голосов
/ 27 мая 2016

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

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

2 голосов
/ 08 августа 2014

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

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

так лучше оберните его крышкой (в нашей истории это интерфейс), тогда он отлично выполнит свою работу.

Теперь работа почтальона состоит в том, чтобы получать и доставлять только обложки ... (он не беспокоился о том, что находится внутри обложки).

Создать тип interface не фактический тип, но реализовать с фактическим типом.

Создание интерфейса означает, что ваши компоненты получают Легко вписывается в остальную часть кода

Я приведу вам пример.

у вас есть интерфейс AirPlane, как показано ниже.

interface Airplane{
    parkPlane();
    servicePlane();
}

Предположим, у вас есть методы в вашем классе контроллеров, такие как

parkPlane(Airplane plane)

и

servicePlane(Airplane plane)

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

Потому что он примет любой Самолет, несмотря на фактический тип, flyer, highflyr, fighter и т. Д.

Также в коллекции:

List<Airplane> plane; // Принимает все ваши самолеты.

Следующий пример очистит ваше понимание.


У вас есть истребитель, который его реализует, поэтому

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

То же самое для HighFlyer и других предметов:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

Теперь подумайте, что классы вашего контроллера используют AirPlane несколько раз,

Предположим, ваш класс Controller - это ControlPlane, как показано ниже,

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

здесь магия приходит как

вы можете создавать новые экземпляры AirPlane столько раз, сколько хотите, и вы не меняете

код ControlPlane класса.

Вы можете добавить экземпляр ..

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

вы также можете удалять экземпляры .. ранее созданных типов.

2 голосов
/ 21 декабря 2008

В Java все эти конкретные классы реализуют интерфейс CharSequence:

CharBuffer, String, StringBuffer, StringBuilder

Эти конкретные классы не имеют общего родительского класса, кроме Object, поэтому ничто не связывает их, кроме того факта, что каждый из них имеет какое-то отношение к массивам символов, представляющим или манипулирующим ими. Например, символы String нельзя изменить после создания экземпляра объекта String, тогда как символы StringBuffer или StringBuilder можно редактировать.

Тем не менее, каждый из этих классов способен соответствующим образом реализовать методы интерфейса CharSequence:

char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()

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

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

BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer

1 голос
/ 09 марта 2014

Q: - ... "Вы могли бы использовать любой класс, который реализует интерфейс?"
A: - Да.

Q: -... «Когда вам нужно будет это сделать?»
A: - Каждый раз, когда вам нужен класс (ы), который реализует интерфейс (ы).

Примечание: нам не удалось создать экземпляр интерфейса, не реализованного классом - True.

  • почему?
  • потому что интерфейс имеет только прототипы методов, а не определения (только имена функций, а не их логику)

AnIntf ​​anInst = new Aclass ();
// мы могли бы сделать это , только если Aclass реализует AnIntf.
// anInst будет иметь ссылку на класс.


Примечание:
Теперь мы можем понять, что произойдет, если Bclass и Cclass реализуют один и тот же Dintf.

Dintf bInst = new Bclass();  
// now we could call all Dintf functions implemented (defined) in Bclass.

Dintf cInst = new Cclass();  
// now we could call all Dintf functions implemented (defined) in Cclass.

Что имеем:
одни и те же прототипы интерфейса (имена функций в интерфейсе) и вызов различных реализаций.

Библиография:
Прототипы - Википедия

1 голос
/ 27 декабря 2016

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

IInterface classRef = new ObjectWhatever()

Вы могли бы использовать любой класс, который реализует IInterface? Когда вам нужно это сделать?

Посмотрите на этот вопрос SE для хорошего примера.

Почему интерфейс для Java-класса предпочтительнее?

использует ли производительность интерфейса?

если да, то сколько?

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

как вы можете избежать этого, не поддерживая два бита кода?

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

Один хороший пример использования: реализация шаблона стратегии:

Пример шаблона стратегии в реальном мире

0 голосов
/ 26 марта 2019

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

Посмотри на любую дверь в твоем доме, школе, церкви ... любую стройку .

Представьте себе, что некоторые двери получили опасность справа-внизу (так что вам нужно кланяться, чтобы взаимодействовать с дверью, которая открывает или закрывает ее),

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

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

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

[1] Как ?. выставляя форму значения. Так что вам на самом деле не нужна документация, потому что сам код не требует пояснений (Awesome).

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

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

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

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

Я не сохраняю interface - это самая важная вещь в языке: чаще используется наследование классов. Но в любом случае они важны!
Например (это код Java, но он может быть просто адаптирован к C# или многим другим языкам):

interface Convertable<T> {

    T convert();
}

public class NumerableText implements Convertable<Integer> {

    private String text = "";

    public NumerableText() { }

    public NumerableText(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Integer convert() {
        return this.text.hashCode();
    }
}

public class NumerableTextArray implements Convertable<Integer> {

    private String[] textArray = "";

    public NumerableTextArray() { }

    public NumerableTextArray(String[] textArray) {
        this.textArray = textArray;
    }

    public String[] getTextArray() {
        return this.textArray;
    }

    public void setTextArray(String[] text) {
        this.textArray = textArray;
    }

    public Integer convert() {
        Integer value = 0;
        for (String text : textArray)
            value += text.hashCode();
        return value;
    }
}

public class Foo {

    public static void main() {
        Convertable<Integer> num1 = new NumerableText("hello");
        Convertable<Integer> num2 = new NumerableTextArray(new String[] { "test n°1", "test n°2" });
        System.out.println(String.valueOf(num1.convert()));
        System.out.println(String.valueOf(num2.convert()));
        //Here are you two numbers generated from two classes of different type, but both with the method convert(), which allows you to get that number.
    }
}
0 голосов
/ 18 декабря 2014

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

Юнит-тестирование

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

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

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

Комбинация интерфейсов и наследования Я обнаружил, что комбинация очень хорошая для использования. Я приведу очень простой пример.

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}

Таким образом, копирование кода не требуется, но при этом сохраняется преимущество использования автомобиля в качестве интерфейса (ICar).

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