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

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

Отредактировано:

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

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

Отредактировано:

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

Ответ 1

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

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

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

public interface PageDatasource {
    public List<Page> getPages();
}

И используйте его так:

PageDatasource datasource = // insert concrete PageDatasource implementation here
List<Pages> pages = datasource.getPages();
display(pages);

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

public class DatabasePageDatasource implements PageDatasource {
    public List<Page> getPages() {
        // Database specific code
    }
}

public class XmlPageDatasource implements PageDatasource {
    public List<Page> getPages() {
        // XML specific code
    }
}

Поскольку я использовал интерфейс, теперь я могу использовать либо реализацию - базу данных, либо XML - взаимозаменяемо, не меняя частей своей программы, которые запрашивают данные страницы. Реализации XML и базы данных, вероятно, делают совершенно разные вещи, но вся моя программа заботится о том, что объект, предоставляющий данные страницы, реализует интерфейс PageDatasource.

Ответ 2

Интерфейс Аналогия реальной жизни:

Скажем, вы хотите выпустить книгу из библиотеки. Что вы будете делать:
1) Перейти в библиотеку
2) Найдите интересующую вас книгу 3) Выберите книгу
4) Перейдите в библиотечный стол, чтобы попросить их опубликовать книгу.

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

Итак, скажем: Поведение библиотекаря:
1) Выпуск книги
2) Книга повторных выпусков
3) Возвращенная книга.
человек, назначенный как библиотекарь (что означает согласие на адаптацию вышеупомянутых трех видов поведения), должен реализовать свое поведение по-своему.

Предположим, что PersonA хочет играть роль библиотекаря, тогда ему нужно адаптировать вышеупомянутые 3 Поведения. Мы, как Клиент, не заботимся о том, как он выполняет свое поведение в библиотечном деле, нас интересует только тот человек, который является библиотекарем, и он выполняет задания библиотекаря.
Поэтому то, что вы будете делать, это "Ссылка по интерфейсу" [перейдите в "Библиотечный стол" (который приведет вас к лицам, исполняющим обязанности библиотекаря)], и позвольте сказать, что в Будущем один человек покинул библиотечный пост, затем как клиент, это не повлияет на вас, если вы подошли к Librarian desk, а не конкретному человеку, действующему как библиотекарь. Такой код для интерфейса, а не для Concreteness, полезен.

class PersonA implements Librarian {
    public void IssueBook(){...}
    public void ReIssueBook(){...}
    public void ReturnBook(){...}

    //Other person related tasks...
}   

Ответ 3

Даже если один интерфейс разработчика может быть очень полезным инструментом. Позвольте мне проиллюстрировать пример.

Предположим, вы разрабатываете систему для управления каталогом библиотеки, и библиотека будет предоставлять как книги, так и DVD-диски. Вы решили создать классы Книга и Dvd, чтобы моделировать объекты, предоставляемые, но теперь вы хотите реализовать полиморфизм, чтобы вы могли обрабатывать предметы, а не книги или DVD-диски. Вопрос в том, должен ли Элемент быть абстрактным классом или интерфейсом?

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

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

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

Ответ 4

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

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

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

Просмотрите сообщение в блоге, на которое я указал вам, и вы должны получить полное представление о том, когда использовать каждый и почему вы будете использовать их. Я бы также рекомендовал Microsoft Press хорошую книгу, такую ​​как "CLR via С#". Вы узнаете многое!

Ответ 5

Интерфейсы причины существуют из-за двух принципиальных концепций ООП, которые являются "идентичностью" и "функциональностью"

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

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

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

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

Ответ 6

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

Например, у меня есть процессор записи, который читает записи из одного места и записывает их в другое место. Ему все равно, что/где, или тому, что/где. Все, что он заботится, состоит в том, что он получает вход от какого-либо типа считывателя (с использованием интерфейса IReader) с одной стороны и передает их некоторому типу записи (используя интерфейс IWriter).

В моей первой реализации разработчик IReader получает материал из базы данных SQL, а разработчик IWriter отправляет его через клиент веб-службы. Тем не менее, мы в конечном итоге создадим других исполнителей на обоих концах для доступа к другим репозиториям (FTP-сайты, каталоги файлов на локальном сетевом диске и т.д.).

Все это время процессор в середине не заботится и не меняется. Он просто говорит через эти стандартные интерфейсы.

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

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

Ответ 7

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

Итак, вы можете сделать:

class MyRow extends AbstractRow implements ISearchable, ISortable
{

}

Кроме того, поиск StackOverflow для других подобных вопросов, таких как Необходимость интерфейсов в С#

Ответ 8

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

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

Ответ 9

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

пример:

public abstract class A {
    public void myFunc() {
        //do some work
    }
}

public class B : A {
    private void someOtherFunc() {
        base.myFunc();
    }
}

public class Z {
    public void exampleFunc() {
        //call public base class function exposed through extending class:
        B myB = new B();
        myB.myFunc();

        //explicitly cast B to A:
        ((A) myB).myFunc();

        //'A' cannot be created directly, do implicit cast down to base class:
        A myA = new B();
        myA.myFunc();
    }
}

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

public class Z {
    public void exampleFunc() {
        C myC = new C();
        //these two calls both call the same function:
        myC.myFunc();
        ((IExampleInterface1)myC).myFunc();

        D myD = new D();
        //hmmm... try and work out what happens here, which myFunc() gets called?
        //(clue: check the base class)
        myD.myFunc();

        //call different myFunc() in same class with identical signatures:
        ((IExampleInterface1)myD).myFunc();
        ((IExampleInterface2)myD).myFunc();
    }
}

interface IExampleInterface1
{
    void myFunc();
}

interface IExampleInterface2
{
    void myFunc();
}

public class C : IExampleInterface1
{
    public void myFunc()
    {
        //do stuff
    }
}

public class D : A, IExampleInterface1, IExampleInterface2
{
    void IExampleInterface1.myFunc()
    {
        //this is called when D is cast as IExampleInterface1
    }

    void IExampleInterface2.myFunc()
    {
        //this is called when D is cast as IExampleInterface2
    }
}

Ответ 10

Это проблема дизайна (я говорю о мире Java).

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

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

Пример:

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

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

Вы можете использовать интерфейс InterfaceSenderCommunication с методом boolean sendWithSuccefull (...), если вы не уверены, что поведение не будет часто меняться.

Конечно, суждение "часто меняться - не часто" зависит от двух элементов:

  • Сколько времени я должен потратить на синхронизацию старый код с новыми спецификациями?
  • Сколько платит клиент?;)

Ответ 11

Предположим, что у меня есть класс FoodEstablishment, теперь я хочу сделать простую операцию CRUD, поэтому как мне это сделать? Я определяю интерфейс для обслуживания, но почему?

public interface IFoodEstablishmentService
{
    Task<int> AddAsync(FoodEstablishment oFoodEstablishment);
    FoodEstablishment SelectByFoodEstablishmentId(long id);
    Task<int> UpdateAsync(FoodEstablishment oFoodEstablishment);
    Task<int> DeleteAsync(FoodEstablishment oFoodEstablishment);
}

Затем я реализую этот контракт или интерфейс для этой конкретной службы

public class FoodEstablishmentService : IFoodEstablishmentService
{
    public async Task<int> AddAsync(FoodEstablishment oFoodEstablishment)
    {
       // Insert Operation
        return result;
    }

    public FoodEstablishment SelectByFoodEstablishmentId(long id)
    {
        // Select Logic
        return oFoodEstablishment;
    }

    public async Task<int> UpdateAsync(FoodEstablishment oFoodEstablishment)
    {
        // Update Logic
        return result;
    }

    public async Task<int> DeleteAsync(FoodEstablishment oFoodEstablishment)
    {
        // Delete Logic
        return result;
    }

}

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

IFoodEstablishmentService oFoodEstablishmentService =  new FoodEstablishmentService();
FoodEstablishment oFoodEstablishment = // Input might be from views;
oFoodEstablishmentService.AddAsync(oFoodEstablishment);

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

FoodEstablishmentService oFoodEstablishmentService =  new FoodEstablishmentService();
FoodEstablishment oFoodEstablishment = // Input might be from views;
oFoodEstablishmentService.AddAsync(oFoodEstablishment);

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

public class FoodEstablishmentQueueService : IFoodEstablishmentService
{
    public async Task<int> AddAsync(FoodEstablishment oFoodEstablishment)
    {
       // Insert Queue Operation
        return result;
    }

    public FoodEstablishment SelectByFoodEstablishmentId(long id)
    {
        // Select Queue Logic
        return oFoodEstablishment;
    }

    public async Task<int> UpdateAsync(FoodEstablishment oFoodEstablishment)
    {
        // Update Queue Logic
        return result;
    }

    public async Task<int> DeleteAsync(FoodEstablishment oFoodEstablishment)
    {
        // Delete Queue Logic
        return result;
    }
}

Итак, если я хочу использовать версию очереди, я бы просто сделал

IFoodEstablishmentService oFoodEstablishmentService =  new FoodEstablishmentQueueService();
FoodEstablishment oFoodEstablishment = // Input might be from views;
oFoodEstablishmentService.AddAsync(oFoodEstablishment);

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

Аналогично рассмотрим другой простой пример использования предопределенных типов, таких как IEnumerable. Предположим, что я передаю список коллекции FoodEstablishment и возвращу пользовательский отсортированный список

public FoodEstablishment[] SortFoodEstablishment(FoodEstablishment[] list)
{
    foreach(var oFoodEstablishment in list)
    {
    // some logic
    }
    return sortedList;
}

Итак, мы будем использовать его таким образом

FoodEstablishment[] list = new FoodEstablishment[]{ //some values }
var listSorted = oFoodEstablishmentService.SortFoodEstablishment(list);

Но что, если мы отправляем список вместо массива

List<FoodEstablishment> list = //some values;
var listSorted = oFoodEstablishmentService.SortFoodEstablishment(list);

Мы получим ошибку, потому что ее строгая реализация использует класс, поэтому вместо этого мы используем IEnumerable < > , который реализуется List и который в основном удаляет зависимость от List to Interface

public IEnumerable<FoodEstablishment> SortFoodEstablishment(IEnumerable<FoodEstablishment> list)
{
    foreach(var oFoodEstablishment in list)
    {
    // some logic
    }
    return sortedList;
}

Таким образом, обычная реализация сильно зависит от ситуации

Ответ 12

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

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

Ответ 13

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

Ответ 14

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

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

Ответ 15

Абстрактный класс v/s интерфейс всегда является предметом обсуждения среди разработчиков. Я бы добавил 5 центов. Используйте абстрактный класс, когда вы хотите расширить базу данных comman и где вы хотите предоставить реализацию по умолчанию абстрактному методу.

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

Ответ 16

Считается хорошим стилем для хранения ссылки на HashSet или TreeSet в переменной типа Set.

Set<String> names = new HashSet<String>();

Таким образом, вы должны изменить только одну строку, если вместо этого вы решили использовать TreeSet.

Кроме того, методы, которые работают с наборами, должны указывать параметры типа Set:

public static void print(Set<String> s)

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

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

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

(Чтобы понять, насколько смущаем эту ошибку, взгляните на исходный код для метода binarySearch класса Collections. Этот метод принимает List, но двоичный поиск не имеет смысла для связанного списка. Код тогда неуклюже пытается выяснить, является ли список связанным списком, а затем переключается на линейный поиск!)

Интерфейс Set и интерфейс Map хорошо разработаны, и вы должны их использовать.

Ответ 17

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

Примером реальной жизни является JDBC. Если вы видите API Statemnet, PreparedStatemnet Everything - это интерфейс, но реализация специфична для вендора, например Oracle ojdbc jar имеет некоторую другую реализацию, у mysql было другое.

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

Ответ 18

Использование интерфейса:

  • Чтобы определить контракт
  • Чтобы связать несвязанные классы с возможностями (например, классы, реализующие интерфейс Serializable, могут иметь или не иметь никакой связи между ними, кроме реализации этого интерфейса
  • Обеспечить взаимозаменяемую реализацию, например. Strategy_pattern

Если вы ищете Java как язык программирования, interface имеет несколько дополнительных возможностей с добавлением методов default и static с момента выпуска Java-8.

Обратитесь к этому вопросу для получения дополнительной информации:

Зачем использовать интерфейсы, множественное наследование и интерфейсы, преимущества интерфейсов?

Ответ 19

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

например. Текст, изображение, MP3, FLV, MOV относится к категории FileType

Если наше приложение позволяет пользователям загружать 4 типа файлов, все они относятся к одной категории с именем ТИП ФАЙЛА

  •   
  • Текст  
  • Изображение  
  • Аудио  
  • Видео

Внутри кода у нас будет объект для каждого из указанных типов.

введите описание изображения здесь

Предположим, что нам нужно отправить файл, затем

С интерфейсом Out

введите описание изображения здесь

С интерфейсом

введите описание изображения здесь

Таким образом, мы можем предположить, что все типы файлов (текст, аудио и т.д.) имеют тип FileFormat. Поэтому мы формируем связь IS-A.

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

введите описание изображения здесь