Что значит "программировать на интерфейс"?

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

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

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

IInterface classRef = new ObjectWhatever()

вы можете использовать любой класс, который реализует IInterface? Когда тебе нужно это сделать? Единственное, о чем я могу думать, это если у вас есть метод, и вы не уверены в том, что объект будет передан expect для его реализации IInterface. Не представляю, как часто тебе придется это делать... (Кроме того, как вы можете написать метод, который принимает объект, реализующий интерфейс? Это возможно?)

Извините, если я полностью пропустил точку.

30 ответов


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

когда я впервые начал подвергаться интерфейсам, я тоже был смущен их релевантностью. Я не понимал, зачем они тебе. Если мы используем язык, такой как 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 массив на самом деле может быть


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

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

вместо

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

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

List myList = new TreeList();

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

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

public ArrayList doSomething(HashMap map);

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

public List doSomething(Map map);

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


Программирование интерфейса говорит: "мне нужна эта функциональность, и мне все равно, откуда она берется."

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


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

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

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

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


вы должны посмотреть в инверсию управления:

в таком сценарии вы бы не написали это:

IInterface classRef = new ObjectWhatever();

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

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

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

если мы оставим МОК вне таблицы, вы можете написать код, который знает, что он может говорить с объектом что делает что-то конкретное, а не тип объекта, или, как он это делает.

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

как для кроме того, как вы можете написать метод, который принимает объект, реализующий интерфейс? Это возможно?", в C# вы бы просто использовали тип интерфейса для типа параметра, например:

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

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


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

// 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 и т. д.

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

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

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

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

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

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

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

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

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

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


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


Программирование интерфейсов является удивительным, это способствует свободной связи. Как отметил @lassevk, инверсия управления является большим использованием этого.

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

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


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


много объяснений там, но чтобы сделать его еще проще. Возьмем, к примеру, a List. Можно реализовать список в виде:

  1. внутренний массив
  2. список ссылок
  3. другое внедрение

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

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

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


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

сказано, на стр. 18:

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

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

а еще выше начиналось:

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

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

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


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


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


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

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


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

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

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

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


C++ и объяснение.

интерфейс как классы и открытые методы.

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

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

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

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

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

Что касается "пропущенных" о том, как все работает; big-time (по крайней мере, в C++), так как именно так работает большинство рамок стандартной библиотеки шаблонов.

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

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


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

Charbuffer С, Строки, StringBuffer, То StringBuilder

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

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

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

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

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

BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, Звено outputstreamwriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, Stringwriter Не, Писатель


в простых терминах... Если я пишу новый пловец класса, чтобы добавить функциональность swim () и нужно использовать объект класса, скажем, Dog, и этот класс Dog реализует интерфейс Animal, который объявляет swim () [чтобы лучше понять...вы можете нарисовать диаграмму того, о чем я говорю]. На вершине иерархии (животное) это очень абстрактно, а внизу (Собака) это очень конкретно. То, как я думаю о "программировании на интерфейсы", заключается в том, что, когда я пишу класс пловцов, я хочу напишите мой код против интерфейса, который находится так же высоко в иерархии, которая в данном случае является животным объектом. Интерфейс свободен от деталей реализации и, таким образом, делает ваш код слабо связанным. Детали реализации могут быть изменены со временем, однако это не повлияет на оставшийся код, так как все вы взаимодействуете с интерфейсом, а не с реализацией. Вам все равно, на что похожа реализация...все, что вы знаете, это то, что будет класс, который будет реализовывать взаимодействие.


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

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

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

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

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

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

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

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

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

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

parkPlane(Airplane plane)

и

servicePlane(Airplane plane)

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

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

кроме того, в коллекции:

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 и другие 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.
    }
}

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

Предположим ваш класс контроллера 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.

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


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

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

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

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

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


Примечание:
теперь мы могли бы понять, что произойдет, если 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.

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

список литературы:
прототипы - Википедия


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


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

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

  1. мешает нам делать контекстуально неуместные вещи, и
  2. позволяет нам безопасно изменить реализацию в будущем.

например,Person класс, реализует Friend и Employee интерфейс.

class Person implements AbstractEmployee, AbstractFriend {

}

в контексте Дня рождения человека, мы программы Friend интерфейс, чтобы предотвратить обращение с человеком как Employee.

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

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

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

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

в будущем, если наш бизнес перейдет на работу с собаками, мы можем достаточно легко изменить программное обеспечение. Во-первых, мы создаем Dog класс, который реализует оба Friend и Employee. Тогда мы смело меняемся new Person() to new Dog(). Даже если обе функции имеют тысячи строк кода, это простое редактирование будет работать, потому что мы знаем, что верно следующее:

  1. функции party использует только Friend подмножество Person.

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

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

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

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

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

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

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


давайте начнем с некоторых определений первой:

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

тип n. определенный интерфейс

простой пример интерфейс как определено выше, будут все методы объекта PDO, такие как query(), commit(), close() etc. в целом, а не по отдельности. Эти методы, т. е. его интерфейс определяют полный набор сообщений, запросов, которые могут быть отправлены объекту.

A тип как определено выше, это определенный интерфейс. Я буду использовать интерфейс made-up shape для демонстрации:draw(), getArea(), getPerimeter() etc..

если объект имеет тип базы данных, мы имеем в виду, что он принимает сообщения/запросы интерфейса базы данных,query(), commit() etc.. Объекты могут быть многих типов. У вас может быть объект базы данных типа shape, пока он реализует свой интерфейс, и в этом случае это будет субтипирование.

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

клиент будет знать только интерфейс, а не реализацию.

таким образом, по сути, программирование интерфейса будет включать создание некоторого типа абстрактного класса, такого как Shape только с указанным интерфейсом, т. е. draw(), getCoordinates(), getArea() etc.. И тогда разные конкретные классы реализуют эти интерфейсы, такие как класс круга, класс квадрата, класс треугольника. следовательно, программа для интерфейса не является реализацией.


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

IInterface classRef = new ObjectWhatever()

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

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

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

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

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

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

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

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

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

реальный пример шаблона стратегии


Я не сохранить interfaces-самая важная вещь в языке: чаще всего используется наследование класса. Но в любом случае они важны!
например (это 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.
    }
}

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