What does it mean to “program to an interface”?

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

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

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

IInterface classRef = new ObjectWhatever()

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

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

 Disillusioned08 янв. 2017 г., 06:26
@ Дэмиен, вы не должны связывать этот вопрос с дополнительными вопросами. Так что это 1 вопрос на тему сайта. Лучше задавать отдельные вопросы и ссылаться на них, если они актуальны.
 Jonathan Allen19 июл. 2012 г., 21:37
Основная проблема с такими вопросами заключается в том, что предполагается, что «программирование интерфейса» означает «оборачивать все в абстрактный интерфейс», что глупо, если учесть, что термин предшествует концепции абстрактных интерфейсов в стиле Java.
 Jeroen van Langen15 авг. 2013 г., 14:34
В основном это делается с помощью COM. Где вы обрабатываете объект, не зная класс, а только то, какой интерфейс он поддерживает.
 Fattie08 дек. 2016 г., 15:30
Это просто модное слово дляПерезвоните, Вот и все. Все это означает, что вы что-то делаете (скажем, «получаете информацию из сети» или «делаете расчет»). Эта другая вещь в конечном итогевызвать функцию ваш, чтобы сказать вам результат. Правильно? Так что вы должны как-тоукажите название этой функции (функция обратного вызова). Интерфейс! просто заявляет название этой функции. Вот и все, больше ничего.
 Andrew Spencer02 мая 2012 г., 10:36
@ apollodude217, на самом деле это лучший ответ на всей странице. По крайней мере, для вопроса в заголовке, поскольку здесь есть как минимум 3 совершенно разных вопроса ...
 Nyerguds06 янв. 2017 г., 15:26
КлассическийList lst = new ArrayList(); просто означает, что вы ограничиваете какие функции для созданного объекта доступны вам, что совершенно глупо. Эта странная конструкция продолжает распространяться учителями и книгами по Java без какой-либо мыслимой причины.
 Mitch Wheat21 дек. 2008 г., 05:26
@Анде Тернер: это плохой совет. 1). «Ваша программа должна быть оптимальной» не является хорошей причиной для замены интерфейсов! Затем вы говорите: «Распределите свой код, запрограммированный на интерфейсы, хотя ...», поэтому вы советуете, что с учетом данного требования (1) вы затем выпускаете неоптимальный код?!?
 apollodude21727 апр. 2012 г., 18:34
Большинство ответов здесь не совсем правильные. Это вовсе не означает и даже не подразумевает «использовать ключевое слово интерфейса». Интерфейс - это спецификация того, как что-то использовать - синоним контракта (ищите его). Отдельно от этого является реализация, то есть, как этот контракт выполняется. Запрограммируйте только гарантии метода / типа, чтобы при изменении метода / типа таким образом, который все еще подчиняется контракту, он не нарушает код, использующий его.
 Ande TURNER21 дек. 2008 г., 05:21
Если вы помните, и ваша программа должна быть оптимальной, перед компиляцией вы можете поменять объявление интерфейса для фактической реализации. Как использование интерфейса добавляет уровень косвенности, которая дает удар по производительности. Раздайте свой код, запрограммированный на интерфейсы, хотя ...

Ответы на вопрос(30)

Мартин Фаулер: инверсия управляющих контейнеров и шаблон внедрения зависимостейВикипедия: инверсия контроля

В таком случае вы бы не написали так:

IInterface classRef = new ObjectWhatever();

Вы бы написали что-то вроде этого:

IInterface classRef = container.Resolve<IInterface>();

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

Если мы оставим IoC вне стола, вы можете написать код, который знает, что он может общаться с объектомэто делает что-то конкретное, но не какой тип объекта или как он это делает.

Это пригодится при передаче параметров.

Что касается вашего вопроса в скобках «Кроме того, как вы могли бы написать метод, который принимает объект, который реализует интерфейс? Это возможно?», В C # вы просто использовали бы тип интерфейса для типа параметра, например так:

public void DoSomethingToAnObject(IInterface whatever) { ... }

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

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

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

Позвольте мне привести вам конкретный пример.

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

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

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

Итак, мы сделали Nr. 3. Все наши элементы управления реализуют ILocalizable, который представляет собой интерфейс, который дает нам один метод, возможность переводить «себя» в контейнер текста / правил перевода. Таким образом, форме не нужно знать, какой тип элемента управления она нашла, только что она реализует определенный интерфейс и знает, что существует метод, который можно вызвать для локализации элемента управления.

 terjetyl21 дек. 2008 г., 03:36
Согласитесь, я бы сказал, что программирование на основе интерфейсов - это просто метод, который делает IoC более простым и надежным.
 Kevin Le - Khnle21 дек. 2008 г., 02:13
Зачем упоминать IoC в самом начале, так как это только добавит путаницы.

so I want to give my point of view here, including some extra information what I noticed when using this method.

Модульное тестирование

For the last two years, I have written a hobby project and I did not write unit tests for it. After writing about 50K lines I found out it would be really necessary to write unit tests. I did not use interfaces (or very sparingly) ... and when I made my first unit test, I found out it was complicated. Зачем?

Because I had to make a lot of class instances, used for input as class variables and/or parameters. So the tests look more like integration tests (having to make a complete 'framework' of classes since all was tied together).

Fear of interfaces So I decided to use interfaces. My fear was that I had to implement all functionality everywhere (in all used classes) multiple times. In some way this is true, however, by using inheritance it can be reduced a lot.

Combination of interfaces and inheritance I found out the combination is very good to be used. I give a very simple example.

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
}

This way copying code is not necessary, while still having the benefit of using a car as interface (ICar).

List, Можно реализовать список с помощью:

внутренний массивСвязанный списокдругая реализация

Создавая интерфейс, скажемList, Вы только код для определения списка или чтоList значит в реальности.

Вы можете использовать любой тип реализации внутренне, скажем,array реализация. Но предположим, что вы хотите изменить реализацию по какой-то причине, например, из-за ошибки или производительности. Тогда вам просто нужно изменить декларациюList<String> ls = new ArrayList<String>() вList<String> ls = new LinkedList<String>().

В другом месте кода вы не должны будете что-либо менять; Потому что все остальное было построено на определенииList.

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

List myList = new ArrayList(); // programming to the List interface

вместо

ArrayList myList = new ArrayList(); // this is bad

Они выглядят точно так же в короткой программе, но если вы продолжите использоватьmyList 100 раз в вашей программе вы можете начать видеть разницу. Первое объявление гарантирует, что вы вызываете только методыmyList которые определеныList интерфейс (поэтому нетArrayList конкретные методы). Если вы запрограммировали интерфейс таким образом, позже вы можете решить, что вам действительно нужно

List myList = new TreeList();

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

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

public ArrayList doSomething(HashMap map);

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

public List doSomething(Map map);

Теперь не имеет значения, какойList вы вернетесь, или какойMap передается в качестве параметра. Изменения, которые вы делаете внутриdoSomething Метод не заставит вас изменить код вызова.

 Yvette Colomb12 янв. 2019 г., 19:51
Комментарии не для расширенного обсуждения; этот разговор былперешел в чат.
 luswata samuel05 июл. 2019 г., 15:10
Очень четкое объяснение. Очень полезно

You could use any class that implements interface?"
A: - Yes.

Q: -... "When would you need to do that?"
A: - Each time you need a class(es) that implements interface(s).

Замечания: we couldn't instantiate an interface not implemented by a class - Правда.

Зачем?because interface has only methods prototypes, not definitions (just functions names, not their logic)

AnIntf anInst = new Aclass();
// мы могли бы сделать этотолько если Aclass implements AnIntf.
// anInst will have Aclass reference.

Замечания:
Now we could understand what happend if Bclass and Cclass implements same 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.

What we have:
same interface prototypes (functions names in interface), and call different implementations.

Bibliography:
Prototypes - wikipedia

Program to an interface allows to change implementation of contract defined by interface seamlessly. It allows loose coupling between contract and specific implementations.

IInterface classRef = new ObjectWhatever()

You could use any class that implements IInterface? When would you need to do that?

Have a look at this SE question for good example.

Why should the interface for a Java class be preferred?

does using an Interface hit performance?

if so how much?

Да. It will have slight performance overhead in sub-seconds. But if your application has requirement to change the implementation of interface dynamically, don't worry about performance impact.

how can you avoid it without having to maintain two bits of code?

Don't try to avoid multiple implementations of interface if your application need them. In absence of tight coupling of interface with one specific implementation, you may have to deploy the patch to change one implementation to other implementation.

One good use case: Implementation of Strategy pattern:

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

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

Например, я мог бы иметь интерфейс движения. Метод, который заставляет что-то «двигаться», и любой объект (Person, Car, Cat), который реализует интерфейс движения, может быть передан и задан для перемещения. Без метода каждый знает тип класса.

ниям здесь), я настоятельно рекомендую взять копиюHead First Design Patterns, Это очень легко прочитать, и он ответит на ваш вопрос напрямую, объяснит, почему это важно, и покажет вам много шаблонов программирования, которые вы можете использовать, чтобы использовать этот принцип (и другие).

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.

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

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

 supercat18 окт. 2014 г., 19:00
...operations like indexOf quickly, there's no way that a caller who isn't familiar with a particular implementation of CharSequence could ask it to do so rather than having to use charAt to examine each individual character.
 supercat18 окт. 2014 г., 18:58
It's too bad interfaces like CharSequence are so anemic. I wish Java and .NET had allowed interfaces to have default implementation, so that people wouldn't pare down interfaces purely for the purpose of minimizing boilerplate code. Given any legitimate CharSequence implementation one could emulate most of the functions of String using only the above four methods, but many implementations could perform those functions much more efficiently in other ways. Unfortunately, even if a particular implementation of CharSequence holds everything in a single char[] and could perform many...

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

Указано на с. 18:

Программа для интерфейса, а не реализация

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

и, кроме того, это началось с:

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

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

Другими словами, не пишите свои классы, чтобыquack() метод для уток, а затемbark() метод для собак, потому что они слишком специфичны для конкретной реализации класса (или подкласса). Вместо этого напишите метод, используя имена, которые являются достаточно общими для использования в базовом классе, напримерgiveSound() или жеmove(), так что они могут быть использованы для уток, собак или даже автомобилей, и тогда клиент ваших классов может просто сказать.giveSound() а не думать о том, чтобы использоватьquack() или жеbark() или даже определить тип, прежде чем выдать правильное сообщение для отправки объекту.

even when we are not depending on abstractions.

Programming to interfaces forces us to use a contextually appropriate subset of an object. That helps because it:

prevents us from doing contextually inappropriate things, andlets us safely change the implementation in the future.

For example, consider a Person класс, который реализуетFriend иEmployee интерфейс.

class Person implements AbstractEmployee, AbstractFriend {

}

In the context of the person's birthday, we program to the Friend interface, to prevent treating the person like an Employee.

function party() {
    const friend: Friend = new Person("Kathryn");
    friend.HaveFun();
}

In the context of the person's work, we program to the Employee interface, to prevent blurring workplace boundaries.

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}

Отлично. We have behaved appropriately in different contexts, and our software is working well.

Far into the future, if our business changes to work with dogs, we can change the software fairly easily. First, we create Dog class that implements both Friend а такжеEmployee, Then, we safely changenew Person() вnew Dog(), Even if both functions have thousands of lines of code, that simple edit will work because we know the following are true:

функцияparty uses only the Friend подмножествоPerson.функцияworkplace uses only the Employee подмножествоPerson.Учебный классDog implements both the Friend а такжеEmployee интерфейсы.

On the other hand, if either party или жеworkplace were to have programmed against Person, there would be a risk of both having Person-specific code. Changing from Person вDog would require us to comb through the code to extirpate any Person-specific code that Dog does not support.

The moral: programming to interfaces helps our code to behave appropriately and to be ready for change. It also prepares our code to depend on abstractions, which brings even more advantages.

 Casey18 июл. 2017 г., 22:41
Assuming you don't have excessively broad interfaces, that is.

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

// if I want to add search capabilities to my application and support multiple search
// engines such as google, yahoo, live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

тогда я мог бы создать GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider и т. д.

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

затем создайте JpegImageLoader, GifImageLoader, PngImageLoader и т. д.

Большинство надстроек и плагинов работают с интерфейсами.

Другое популярное использование - шаблон Repository. Скажем, я хочу загрузить список почтовых индексов из разных источников

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

затем я мог бы создать XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository и т. д. Для своих веб-приложений я часто создаю XML-репозитории на ранней стадии, чтобы я мог что-то запустить и запустить до того, как база данных Sql будет готова. Когда база данных готова, я пишу SQLRepository, чтобы заменить версию XML. Остальная часть моего кода остается неизменной, так как он работает исключительно на интерфейсах.

Методы могут принимать интерфейсы, такие как:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}

Код для интерфейса Не реализация не имеет ничего общего с Java, как и с интерфейсом конструкции.

Эта концепция была широко известна в книгах «Паттерны / Банды четырех», но, скорее всего, была задолго до этого.Эта концепция, безусловно, существовала задолго до появления Java.

Конструкция Java Interface была создана, чтобы помочь в этой идее (среди прочего), и люди стали слишком сосредоточены на конструкции как на центре смысла, а не на первоначальном намерении. Однако по этой причине у нас есть открытые и закрытые методы и атрибуты в Java, C ++, C # и т. Д.

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

И вы можете программировать для интерфейса, а не для реализации, даже не используя конструкцию интерфейса. Вы можете программировать на интерфейс, а не на реализацию в C ++, которая не имеет конструкции Interface. Вы можете интегрировать две массивные корпоративные системы гораздо надежнее, если они взаимодействуют через открытые интерфейсы (контракты), а не вызывают методы для объектов, внутренних по отношению к системам. Ожидается, что интерфейсы всегда будут реагировать одинаково ожидаемым образом при одинаковых входных параметрах; если реализовано для интерфейса, а не для реализации. Концепция работает во многих местах.

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

 madumlao23 апр. 2017 г., 19:41
Первое предложение говорит само за себя. Это должен быть принятый ответ.

абстрактным интерфейсам, как мы видим в Java или .NET. Это даже не концепция ООП.

Что это действительно означает, так это не шутите над внутренностями объекта или структуры данных. Используйте абстрактный программный интерфейс или API для взаимодействия с вашими данными. В Java или C # это означает использование открытых свойств и методов вместо необработанного доступа к полям. Для C это означает использование функций вместо необработанных указателей.

РЕДАКТИРОВАТЬ: И с базами данных это означает использование представлений и хранимых процедур вместо прямого доступа к таблице.

 Sylvain Rodrigue13 мар. 2015 г., 13:23
Лучший ответ. Гамма дает подобное объяснение здесь:artima.com/lejava/articles/designprinciples.html (см. стр. 2). Он ссылается на концепцию ОО, но вы правы: это больше, чем это.

Postman is asked to go home by home and receive the covers contains (letters,documents,cheques,giftcard,application,loveletter) with address written on it to deliver.

Suppose there is no cover and ask post man to go home by home and receive all the things and deliver to other person the postman can get confuse,

so better wrap it with cover(in our story it is interface) then he will do his job fine.

Now postman job is to receive and deliver the covers only..(he dont bothered what is inside in the cover).

Create type of interface not actual type, but implement with actual type.

Create to interface means your components get Fits into the rest of code easily

I give you example.

you have AirPlane interface as below.

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

Suppose you have methods in your Controller class of Planes like

parkPlane(Airplane plane)

а также

servicePlane(Airplane plane)

implemented in your program. It will not ПЕРЕРЫВ your code. I mean, it need not to change as long as it accepts arguments as AirPlane.

Because it will accept any Airplane despite of actual type, flyer, highflyr, fighter, так далее.

Also, in a collection:

List<Airplane> plane; // Will take all your planes.

The following example will clear your understanding.

You have a fighter plane that implements it, so

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.
    }
}

The same thing for HighFlyer and other clasess:

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.
    }
}

Now think your controller classes using AirPlane several times,

Suppose your Controller class is ControlPlane like below,

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

here magic comes as

you may make your new AirPlane type instances as many as you want and you are not changing

кодControlPlane учебный класс.

you can add instance..

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

you may remove instances.. of previously created types too.

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

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

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

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

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

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

РЕДАКТИРОВАТЬ: Ниже приведена ссылка на статью, где Эрих Гамма обсуждает свою цитату «Программа для интерфейса, а не для реализации».

http://www.artima.com/lejava/articles/designprinciples.html

 Ad Infinitum21 апр. 2017 г., 14:07
Очень хорошее интервью. Пожалуйста, будьте осторожны с будущими читателями, в интервью есть четыре страницы. Я бы почти закрыл браузер, прежде чем увидеть его.
 PawelP15 нояб. 2013 г., 11:47
Отличное интервью, Линж, спасибо!
 Sylvain Rodrigue13 мар. 2015 г., 13:17
Пожалуйста, прочитайте еще раз это интервью: Гамма, конечно, говорил о концепции интерфейса ОО, а не о JAVA или особом классе C # (ISomething). Проблема в том, что большинство людей, хотя он говорил о ключевом слове, теперь у нас много ненужных интерфейсов (ISomething).

«Мне нужна эта функциональность, и мне все равно, откуда она».

Рассмотрим (на Java)List интерфейс по сравнению сArrayList а такжеLinkedList конкретные классы. Если все, что меня волнует, это то, что у меня есть структура данных, содержащая несколько элементов данных, к которым я должен обращаться через итерацию, я бы выбралList (и это 99% времени). Если я знаю, что мне нужно постоянное время вставки / удаления из любого конца списка, я мог бы выбратьLinkedList конкретная реализация (или, более вероятно, использоватьОчередь интерфейс). Если я знаю, что мне нужен произвольный доступ по индексу, я бы выбралArrayList конкретный класс.

 beluchin29 дек. 2013 г., 08:56
полностью согласен, то есть независимость между тем, что сделано, и тем, как это делается. Разбивая систему на независимые компоненты, вы получаете систему, которая проста и пригодна для повторного использования (см.Просто сделано легко парнем, который создал Clojure)

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

 Erwin Smout12 мар. 2019 г., 13:49
JDBC плох, но не по какой-либо причине связан с интерфейсом или реализацией или уровнями абстракции. И поэтому для иллюстрации рассматриваемой концепции она просто идеальна.
 Ken Liu21 дек. 2008 г., 05:57
Он полезен не только в том случае, если становится доступным более качественный драйвер, но и позволяет полностью сменить поставщиков баз данных.
 Joshua04 янв. 2011 г., 22:43
JDBC настолько плох, что нуждается в замене. Найдите другой пример.
 Karl21 дек. 2008 г., 17:09
+1, JDBC - пример, который я собирался опубликовать.

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

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

это способствует слабой связи. Как упомянул @lassevk, Inversion of Control отлично подходит для этого.

Кроме того, посмотрите в твердых принципах. вот видео серия

Он проходит через жестко закодированный (сильно связанный пример), затем просматривает интерфейсы и, наконец, переходит к инструменту IoC / DI (NInject).

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

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

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

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

Интерфейс п. The set of all signatures defined by an object's operations is called the interface to the object

Тип п. A particular interface

A simple example of an интерфейс as defined above would be all the PDO object methods such as query(), commit(), close() etc., as a whole, not separately. These methods, i.e. its interface define the complete set of messages, requests that can be sent to the object.

A тип as defined above is a particular interface. I will use the made-up shape interface to demonstrate: draw(), getArea(), getPerimeter() так далее..

If an object is of the Database type we mean that it accepts messages/requests of the database interface, query(), commit() etc.. Objects can be of many types. You can have a database object be of the shape type as long as it implements its interface, in which case this would be sub-typing.

Many objects can be of many different interfaces/types and implement that interface differently. This allows us to substitute objects, letting us choose which one to use. Also known as polymorphism.

The client will only be aware of the interface and not the implementation.

So in essence programming to an interface would involve making some type of abstract class such as Shape with the interface only specified i.e. draw(), getCoordinates(), getArea() etc.. And then have different concrete classes implement those interfaces such as a Circle class, Square class, Triangle class. Hence program to an interface not an implementation.

I don't retain interfaces are the most important thing in a language: it's more commonly used the class inheriting. But anyway they are important!
For example (this is Java code, but it can simply adapted to C# or many other languages):

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.
    }
}
Решение Вопроса

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

Когда я впервые начал знакомиться с интерфейсами, я тоже был озадачен их актуальностью. Я не понимаю, зачем они тебе нужны. Если мы используем такой язык, как Java или C #, у нас уже есть наследование, и я рассматривал интерфейсы какслабее форма наследования и мысли "зачем беспокоиться?" В некотором смысле я был прав, вы можете рассматривать интерфейсы как своего рода слабую форму наследования, но помимо этого я наконец понял их использование в качестве языковой конструкции, рассматривая их как средство классификации общих черт или поведений, которые были продемонстрированы потенциально много не связанных классов объектов.

Например - скажем, у вас есть игра на SIM-карте и есть следующие классы:

class HouseFly inherits Insect {
    void FlyAroundYourHead(){}
    void LandOnThings(){}
}

class Telemarketer inherits Person {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}
}

Очевидно, что эти два объекта не имеют ничего общего с точки зрения прямого наследования. Но вы могли бы сказать, что они оба раздражают.

Допустим, наша игра должна иметь какую-то случайнуювещь это раздражает игрока, когда они обедают. Это может бытьHouseFly илиTelemarketer или и то и другое - но как вы допускаете оба с одной функцией? И как вы просите каждый другой тип объекта «делать их надоедливые вещи» таким же образом?

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

interface IPest {
    void BeAnnoying();
}

class HouseFly inherits Insect implements IPest {
    void FlyAroundYourHead(){}
    void LandOnThings(){}

    void BeAnnoying() {
        FlyAroundYourHead();
        LandOnThings();
    }
}

class Telemarketer inherits Person implements IPest {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}

    void BeAnnoying() {
        CallDuringDinner();
        ContinueTalkingWhenYouSayNo();
    }
}

Теперь у нас есть два класса, каждый из которых может раздражать по-своему. И они не должны происходить из одного и того же базового класса и иметь общие присущие им характеристики - им просто необходимо выполнить договорIPest - этот контракт прост. Вы просто должныBeAnnoying, В связи с этим мы можем смоделировать следующее:

class DiningRoom {

    DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

    void ServeDinner() {
        when diningPeople are eating,

        foreach pest in pests
        pest.BeAnnoying();
    }
}

Здесь у нас есть столовая, которая принимает несколько закусочных и количество вредителей - обратите внимание на использование интерфейса. Это означает, что в нашем маленьком мире, членpests массив может быть на самом делеTelemarketer объект илиHouseFly объект.

ServeDinner Метод вызывается, когда обед подается, и наши люди в столовой должны есть. В нашей маленькой игре именно тогда наши вредители выполняют свою работу - каждый вредитель должен быть раздраженIPest интерфейс. Таким образом, мы можем легко иметь какTelemarketers а такжеHouseFlys надоедать по-своему - нас волнует только то, что у нас что-то естьDiningRoom объект, который является вредным организмом, нам на самом деле все равно, что это такое, и они не могут иметь ничего общего с другими.

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

 Peter Meyer18 февр. 2014 г., 20:06
Методы не предназначены для представления абстрактных методов - их реализация не имеет отношения к вопросу, касающемуся интерфейсов.
 nckbrz09 мар. 2014 г., 20:15
Инкапсуляция поведения, такого как IPest, известна как шаблон стратегии на тот случай, если кто-то заинтересован в получении дополнительных материалов по этому вопросу ...
 Carlos H Romano09 авг. 2015 г., 17:37
Очень хорошее объяснение ... Я простонеобходимость Чтобы сказать это здесь: я никогда не слышал об интерфейсах, являющихся своего рода механизмом свободного наследования, но вместо этого я знаю, что наследование используется в качестве плохого механизма для определения интерфейсов (например, в обычном Python вы делаете это все время).
 supercat20 июл. 2012 г., 17:02
... ясное разумное поведение по умолчанию (иногда ничего не делать, иногда генерировать исключения и часто вызывать статический метод или какой-либо другой элемент интерфейса фиксированным образом); такая функция сделала бы гораздо более практичным, чтобы большее количество типов реализовывало «возможно» интерфейсы, уменьшая необходимость проверки типов и приведения типов во время выполнения.
 D. Ben Knoble26 мая 2015 г., 03:16
Интересно, что вы не указываете на это, потому что объекты вIPest[] IPest ссылки, вы можете позвонитьBeAnnoying() потому что у них есть этот метод, в то время как вы не можете вызывать другие методы без приведения. Тем не менее, каждый объект индивидуаленBeAnnoying() метод будет вызван.
 James02 июн. 2019 г., 19:20
a means of classifying common traits or behaviors that were exhibited by potentially many non-related classes of objects это попадает в точку полностью! В этом иногда не очевидно, что два класса (или больше) имеют общую связь. Хороший ответ :)
 user329314516 июл. 2015 г., 16:37
Означает ли это, что при выполнении кода вforeach петля, обаBe.Annoying() метод будет вызван?
 太極者無極而生01 янв. 2016 г., 15:19
Хотите узнать больше о «Программе, а не о реализации» - как насчет этой части? ЯвляетсяFlyAroundYourHead() слишком конкретен и для реализованного пункта? То же самое с.bark() а также.meow(), которая является программой для реализации - потому что она слишком привязана кDog а такжеCat, Если программирование для интерфейса, это должно быть.giveSound() То есть достаточно общий, чтобы он не был специфичным для конкретной реализации (или для конкретного подкласса)
 Peter Meyer28 июл. 2015 г., 21:47
ТолькоBeAnnoying() метод класса, который "реализует" или "реализует" интерфейс, будет вызван.
 aderchox15 сент. 2018 г., 11:07
Конечно, это отличный способ объяснить, почему интерфейсы полезны, однако, это действительно не ответ на главный вопрос (этот ответstackoverflow.com/a/383982/9868445 намного лучше в этом плане)
 Lost Koder18 нояб. 2016 г., 06:16
Другой дизайн для ситуации: «События» раздражают здесь, и у каждого человека должны быть свои собственные раздражающие события, которые, когда происходят во время обеда, сводят его с ума.
 BAD_SEED10 февр. 2014 г., 12:11
ПочемуHouseFly в классе еще есть какие-то абстрактные методы?
 supercat20 июл. 2012 г., 16:48
... часто быстрее вызывать метод-заглушку, чем определять, следует ли вызывать метод. Это часть причиныIEnumerator<T> наследуетсяIDisposable, В подавляющем большинстве случаев потребителю счетчика будет дешевле безоговорочно позвонитьDispose на нем, чем для потребителя, чтобы проверить, является лиDispose звонок нужен. Плохо, что ни .net, ни Java не позволяют указывать реализации по умолчанию для методов интерфейса, так как во многих случаях интерфейсы могут извлечь выгоду из «необязательных» методов, где могут быть ...
 Shaun Luttin03 июл. 2017 г., 20:31
Пример этого ответа больше похож на инверсию зависимостей (которая также программирует на интерфейсы), чем на пример программирования на интерфейсы. Я хотел бы увидеть полезный пример программирования для интерфейсов, который также не включает инверсию зависимостей.
 MC Emperor22 сент. 2018 г., 21:15
Плюс один за связь между телемаркетером и мухой
 supercat20 июл. 2012 г., 16:39
Еще одна вещь, которую следует учитывать, это то, что в некоторых случаях может быть полезно иметь интерфейс для вещей, которые «могут» раздражать, и иметь разнообразные реализуемые объекты.BeAnnoying как неоперативный; этот интерфейс может существовать вместо или в дополнение к интерфейсу для вещей, которые раздражают (если оба интерфейса существуют, "вещи, которыеявляются раздражающий "интерфейс должен наследовать от" вещей, которыевозможно раздражающий «интерфейс». Недостатком использования таких интерфейсов является то, что реализации могут быть обременены реализацией «раздражающего» числа методов-заглушек. Преимущество состоит в том, что ...
 nawfal10 нояб. 2015 г., 19:05
Я не уверен, еслиTeleMarketer а такжеHouseFly несвязанность - вот ключ к использованию интерфейсов. Даже если бы они были связаны, ситуация все еще требует интерфейсов, учитывая ограничения вокруг базовых классов. Ключ здесьповедения, Существует концептуальная разница между интерфейсом и абстрактным классом, где используется бывший, когда вы хотите передатьимеет поведение (в отличие от последнего, которыйстрого является).

In simple terms... If I'm writing a new class Swimmer to add the functionality swim() and need to use an object of class say Dog, and this Dog class implements interface Animal which declares swim()[To better understand...you may draw a diagram as to what I am talking about]. At the top of the hierarchy(Animal) it's very abstract while at the bottom (Dog) it's very concrete. The way I think about "programming to interfaces" is that, as I write Swimmer class, I want to write my code against the interface that's as far up that hierarchy which in this case is Animal object. An interface is free from implementation details and thus makes your code loosely-coupled. The implementation details can be changed with time, however it would not affect the remaining code since all you are interacting is with the interface and not the implementation. You don't care what the implementation is like...all you know is that there will be a class that would implement the interface.

Думайте об интерфейсе как о ваших открытых методах классов.

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

Любой класс алгоритма, который определяет функции / интерфейс, к которым Vector обращается, будет удовлетворять «контракту» (как кто-то объяснил в исходном ответе). Алгоритмы даже не должны быть одного и того же базового класса; единственное требование состоит в том, чтобы функции / методы, от которых зависит Вектор (интерфейс), были определены в вашем алгоритме.

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

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

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

Что касается «упущения» в отношении того, как все работает; большой (по крайней мере, в C ++), поскольку именно так работает большая часть стандартной библиотеки TEMPLATE.

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

Это огромная тема и один из основополагающих принципов проектирования шаблонов.

where you want your implementation class to implement methods written in the contract (interface). Since Java does not provide multiple inheritance, "programming to interface" is a good way to achieve multiple inheritance.

If you have a class A that is already extending some other class B, but you want that class A to also follow certain guidelines or implement a certain contract, then you can do so by the "programming to interface" strategy.

"Program to interface" means don't provide hard code right the way, meaning your code should be extended without breaking the previous functionality..

Ваш ответ на вопрос