Почему я не могу понять интерфейсы?

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

Я говорю об интерфейсах в контексте интерфейсов и абстрактных классов.

Не могли бы вы пояснить, имеете в виду API, графический интерфейс или о каком интерфейсе вы говорите?

devinmoore 23.09.2008 22:36

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

Jacob T. Nielsen 23.09.2008 22:38

Я говорю об интерфейсах в контексте интерфейсов и абстрактных классов.

Justin Obney 23.09.2008 22:47

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

terjetyl 05.09.2010 20:56
В PHP
В PHP
В большой кодовой базе с множеством различных компонентов классы, функции и константы могут иметь одинаковые имена. Это может привести к путанице и...
Принцип подстановки Лискова
Принцип подстановки Лискова
Принцип подстановки Лискова (LSP) - это принцип объектно-ориентированного программирования, который гласит, что объекты суперкласса должны иметь...
54
4
7 286
25
Перейти к ответу Данный вопрос помечен как решенный

Ответы 25

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

Интерфейсы описывают функциональность, а не реализацию.

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

Public Interface IRollOver
    Sub RollOver()
End Interface

Public Class Dog Implements IRollOver
    Public Sub RollOver() Implements IRollOver.RollOver
        Console.WriteLine("Rolling Over!")
    End Sub
End Class

Public Sub Main()
    Dim d as New Dog()
    Dim ro as IRollOver = TryCast(d, IRollOver)
    If ro isNot Nothing Then
        ro.RollOver()
    End If
End Sub

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

Я думаю, что собаки и кошки - это де-факто демонстрация полиморфизма.

Bob King 23.09.2008 23:37

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

Wedge 24.09.2008 00:35

Клин, это все еще контракт или, по крайней мере, его часть.

Derek Park 24.09.2008 03:35

@Wedge - вы гарантируете, что ваш класс может «обрабатывать» эти методы и предоставлять эти свойства. Это все, что нужно интерфейсу. Если реализация Cat "RollOver ()" заставляет его прыгать на диван, это не ответственность вызывающего абонента.

Bob King 24.09.2008 16:06
Ответ принят как подходящий

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

Подумайте об этом так: вы хотите поделиться данными с кем-то в кубе рядом с вами, поэтому вы вытаскиваете флешку и копируете / вставляете. Вы идете по соседству, и парень спрашивает: "Это USB?" а вы говорите да - все готово. Не имеет значения ни размер флешки, ни производитель - важно только то, что это USB.

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

public void Paint(Car car, System.Drawing.Color color)...

Это будет работать, пока ваш клиент не скажет: «Теперь я хочу покрасить грузовики», чтобы вы могли сделать следующее:

public void Paint (Vehicle vehicle, System.Drawing.Color color)...

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

public interface IPaintable{
   void Paint(System.Drawing.Color color);
}

... и передал это в вашу рутину:

public void Paint(IPaintable item, System.Drawing.Color color){
   item.Paint(color);
}

Надеюсь, это имеет смысл - это довольно упрощенное объяснение, но мы надеемся, что оно дойдет до сути.

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

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

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

Dhananjay 24.03.2012 08:23

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

Допустим, у вас есть пара классов под названием «Собака», «Кошка» и «Мышь». Каждый из этих классов является Pet, и теоретически вы можете унаследовать их все от другого класса, называемого Pet, но вот в чем проблема. Домашние животные сами по себе ничего не делают. Вы не можете пойти в магазин и купить питомца. Вы можете пойти и купить собаку или кошку, но домашнее животное - это абстрактное понятие, а не конкретное.

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

public interface IPet
{
    void Eat(object food);
    void Sleep(int duration);
}

Каждый из ваших классов Dog, Cat и Mouse реализует IPet.

public class Dog : IPet

Итак, теперь каждый из этих классов должен иметь собственную реализацию Eat and Sleep. Ура у вас есть контракт ... А теперь в чем смысл.

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

public class PetStore
{
     public static IPet GetRandomPet()
     {    
          //Code to return a random Dog, Cat, or Mouse
     } 
}

IPet myNewRandomPet = PetStore.GetRandomPet();
myNewRandomPet.Sleep(10);

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

Таким образом, этот ответ, возможно, был вообще бесполезен, но общая идея заключается в том, что интерфейсы позволяют вам делать изящные вещи, такие как Injection Dependency и Inversion of Control, где вы можете получить объект, иметь четко определенный список вещей, которые объект может делать, никогда ДЕЙСТВИТЕЛЬНО зная, каков конкретный тип этого объекта.

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

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

interface IReport
{
    string RenderReport();
}

class MyNewReport : IReport
{
    public string RenderReport()
    {
        return "Hello World Report!";

    }
}

class AnotherReport : IReport
{
    public string RenderReport()
    {
        return "Another Report!";

    }
}

//This class can process any report that implements IReport!
class ReportEmailer()
{
     public void EmailReport(IReport report)
     {
         Email(report.RenderReport());
     }
}

class MyApp()
{
    void Main()
    {
        //create specific "MyNewReport" report using interface
        IReport newReport = new MyNewReport();

        //create specific "AnotherReport" report using interface
        IReport anotherReport = new AnotherReport();

        ReportEmailer reportEmailer = new ReportEmailer();

        //emailer expects interface
        reportEmailer.EmailReport(newReport);
        reportEmailer.EmailReport(anotherReport);



    }

}

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

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

Например, интересующая вас последовательность не имеет ничего общего с первичными ключами в записях. Для объекта, реализующего интерфейс, вы можете сказать myObject.next () или myObject.prev ().

Это довольно «длинная» тема, но позвольте мне попытаться выразить ее проще.

Интерфейс - как «они его называют» - это контракт. Но забудьте об этом слове.

Лучший способ понять их - использовать какой-то пример псевдокода. Вот как я их понимал давным-давно.

Предположим, у вас есть приложение, которое обрабатывает сообщения. Сообщение содержит что-то вроде темы, текста и т. д.

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

Это может легко превратиться в код Спагетти. Итак, что вы делаете вместо того, чтобы иметь MessageController, а не только «Сообщения», вы даете ему возможность работать с интерфейс под названием IMessage (I - это просто обычное использование, но не обязательно).

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

Поэтому, когда вы создаете свои классы EMail, Fax, PhoneCall, вы делаете их Воплощать в жизнь, Интерфейс с именем IMessage.

Итак, в вашем MessageController вы можете иметь такой метод:

private void ProcessMessage(IMessage oneMessage)
{
    DoSomething();
}

Если бы вы не использовали интерфейсы, вам потребовалось бы:

private void ProcessEmail(Email someEmail);
private void ProcessFax(Fax someFax);
etc.

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

Почему и как?

Поскольку интерфейс - это договор, который определяет некоторые вещи, которых вы придерживаетесь (или реализуете) должен, чтобы иметь возможность его использовать. Думайте об этом как о значок. Если ваш объект «Факс» не имеет интерфейса IMessage, тогда ваш метод ProcessMessage не сможет работать с этим, он выдаст вам недопустимый тип, потому что вы передаете факс методу, ожидающему IMessage. объект.

Вы понимаете суть?

Думайте об интерфейсе как о «подмножестве» методов и свойств, которые вам будут доступны, несмотря - реальный тип объекта. Если исходный объект (факс, электронная почта, телефонный звонок и т. д.) Реализует этот интерфейс, вы можете безопасно передать его через методы, которым нужен этот интерфейс.

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

Факс myFax = (Факс) SomeIMessageThatIReceive;

У ArrayList () в .NET 1.1 был красивый интерфейс под названием IList. Если бы у вас был IList (очень "общий"), вы могли бы преобразовать его в ArrayList:

ArrayList ar = (ArrayList)SomeIList;

И есть тысячи образцов в дикой природе.

Такие интерфейсы, как ISortable, IComparable и т. д., Определяют методы и свойства, которые должен реализует в своем классе для достижения этой функциональности.

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

Если вы хотите отсортировать (или, например, перечислить) свои объекты, они понадобятся вам для реализации соответствующего интерфейса. В примере .NET, если у вас есть список объектов «Факс» и вы хотите иметь возможность Сортировать для них с помощью MyList.Sort (), вы необходимость сделаете свой класс факса следующим образом:

public class Fax : ISorteable 
{
   //implement the ISorteable stuff here.
}

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

предупреждение: Не все хорошо в интерфейсах, с ними есть проблемы с немного, пуристы ООП начнут с этим войну. Я останусь в стороне. Одним из недостатков Interfce (по крайней мере, в .NET 2.0) является то, что у вас не может быть ЧАСТНЫХ или защищенных членов, должен может быть общедоступным. В этом есть смысл, но иногда вам хочется просто объявить материал как частный или защищенный.

ваш пример кода ArrayList ar = (ArrayList) SomeIList; это неверно. Вы можете использовать IList только в том случае, если он уже был ArrayList. Суть использования IList заключалась в том, чтобы я мог создать свой собственный объект, реализующий IList, и передать его любому методу, который принимает IList, без предварительного помещения его в ArrayList.

Robert Paulson 24.09.2008 02:38

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

Скажем, у вас есть:

public interface ICommand
{
    void Execute();
}
public class PrintSomething : ICommand
{
    OutputStream Stream { get; set; }
    String Content {get; set;}
    void Execute()
    { 
        Stream.Write(content);
    }
}

Теперь у вас есть заменяемая командная структура. Любой экземпляр класса, реализующего IExecute, может быть сохранен в некотором списке, скажем что-то, что реализует IEnumerable, и вы можете пройти через это и выполнить каждый из них, зная, что каждый объект будет просто делать то, что нужно. Вы можете создать составную команду, реализовав CompositeCommand, у которого будет свой собственный список команд для запуска, или LoopingCommand для многократного выполнения набора команд, тогда у вас будет большая часть простого интерпретатора.

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

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

Посмотрите принцип подстановки Лискова, чтобы понять это.

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

Обновлять Поскольку вы, кажется, спрашиваете об абстрактных классах и интерфейсах, вот мое предпочтительное упрощение:

  • Интерфейсы определяют возможности и особенности.
  • Абстрактные классы определяют базовую функциональность.

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

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

Цель состоит в том, чтобы без необходимости видеть код в классе, вы могли знать, можно ли его использовать для определенной задачи. Например, класс Integer в Java реализует сопоставимый интерфейс, поэтому, если вы видели только заголовок метода (открытый класс String реализует Comparable), вы бы знали, что он содержит метод compareTo ().

Итак, речь идет об абстрактных классах и интерфейсах ...

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

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

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

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

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

Умение решать сложную задачу одним словом очень полезно.

Интерфейсы также являются ключом к полиморфизму, одному из «ТРЕХ СТОПОВ УДАЧ».

Некоторые люди уже упоминали об этом выше, полиморфизм просто означает, что данный класс может принимать разные «формы». Это означает, что если у нас есть два класса, «Dog» и «Cat», и оба реализуют интерфейс «INeedFreshFoodAndWater» (хе-хе) - ваш код может делать что-то вроде этого (псевдокод):

INeedFreshFoodAndWater[] array = new INeedFreshFoodAndWater[];
array.Add(new Dog());
array.Add(new Cat());

foreach(INeedFreshFoodAndWater item in array)
{
   item.Feed();
   item.Water();
}

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

Простой ответ: интерфейс - это набор сигнатур методов (+ тип возврата). Когда объект говорит, что он орудия интерфейс, вы знаете, что он предоставляет этот набор методов.

Одна из веских причин для использования интерфейса вместо абстрактного класса в Java заключается в том, что подкласс не может расширять несколько базовых классов, но он МОЖЕТ реализовывать несколько интерфейсов.

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

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

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

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

Посмотрим, поможет ли код:

public interface IReport
{
    void RenderReport(); // This just defines the method prototype
}

public abstract class Reporter
{
    protected void DoSomething()
    {
        // This method is the same for every class that inherits from this class
    }
}

public class ReportViolators : Reporter, IReport
{
    public void RenderReport()
    {
        // Some kind of implementation specific to this class
    }
}

public class ClientApp
{
    var violatorsReport = new ReportViolators();

    // The interface method
    violatorsReport.RenderReport();

    // The abstract class method
    violatorsReport.DoSomething();
}

Интерфейсы - это способ реализации соглашений, по-прежнему строго типизированный и полиморфный.

Хорошим примером из реальной жизни может быть IDisposable в .NET. Класс, реализующий интерфейс IDisposable, заставляет этот класс реализовать метод Dispose (). Если класс не реализует Dispose (), вы получите ошибку компилятора при попытке сборки. Кроме того, этот шаблон кода:

using (DisposableClass myClass = new DisposableClass())
  {
  // code goes here
  }

Приведет к автоматическому выполнению myClass.Dispose () при выходе из внутреннего блока.

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

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

У меня была та же проблема, что и у вас, и я нахожу объяснение "контракта" немного сбивающим с толку.

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

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

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

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

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

Bird bird = GetMeAnInstanceOfABird(someCriteriaForSelectingASpecificKindOfBird);
bird.Fly(Direction.South, Speed.CruisingSpeed);

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

Однако эта парадигма становится запутанной, когда у вас нет истинного отношения это. Допустим, вы хотите написать код, который будет летать по небу. Если вы напишете этот код, чтобы принять базовый класс Птица, внезапно становится трудно развить этот код для работы с экземпляром JumboJet, потому что, хотя Птица и JumboJet, безусловно, могут летать, JumboJet, безусловно, не является Птица.

Войдите в интерфейс.

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

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

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

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

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

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

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

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

Другие вопросы по теме