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

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

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

Ответ 1

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

Подумайте об этом так: вы хотите поделиться данными с кем-то в кубе рядом с вами, поэтому вы вытаскиваете свою флеш-палку и копируете/вставляете. Вы идете по соседству, и парень говорит: "Это 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);
}

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

Ответ 2

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

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

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

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

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

public class Dog : IPet

Итак, теперь каждый из этих классов должен иметь собственную реализацию Eat и 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, где вы можете получить объект, иметь четко определенный список вещей, которые этот объект может выполнять никогда не ДЕЙСТВИТЕЛЬНО зная, каков конкретный тип этого объекта.

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

С точки зрения .NET

  • Определение интерфейса представляет собой список операций и/или свойств.
  • Методы интерфейса всегда общедоступны.
  • Сам интерфейс не должен быть публичным.

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

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

Пример, который немного более конкретен:

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

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

// First pass - not maintainable
void SubmitToWorkflow(object o, User u)
{
  if (o is StreetMap)
  {
     var map = (StreetMap)o;
     map.LastUpdated = DateTime.UtcNow;
     map.UpdatedByUser = u.UserID;
  }
  else if (o is Person)
  {
     var person = (Person)o;
     person.LastUpdated = DateTime.Now; // Whoops .. should be UtcNow
     person.UpdatedByUser = u.UserID;
  }
  // Whoa - very unmaintainable.

В приведенном выше коде SubmitToWorkflow() должен знать о каждом объекте. Кроме того, код представляет собой беспорядок с одним массовым переключателем if/else/, нарушающим принцип " не повторять себя" (DRY), и требует от разработчиков запоминать изменения копирования/вставки каждый раз, когда в систему добавляется новый объект.

// Second pass - brittle
void SubmitToWorkflow(object o, User u)
{
  if (o is DTOBase)
  {
     DTOBase dto = (DTOBase)o;
     dto.LastUpdated = DateTime.UtcNow;
     dto.UpdatedByUser = u.UserID;
  }

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

// Third pass pass - also brittle
void SubmitToWorkflow(DTOBase dto, User u)
{
  dto.LastUpdated = DateTime.UtcNow;
  dto.UpdatedByUser = u.UserID;

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

Интерфейсы, как они могут помочь?

Если мы определим очень простой интерфейс:

public interface IUpdateTracked
{
  DateTime LastUpdated { get; set; }
  int UpdatedByUser { get; set; }
}

Интерфейс может реализовать любой класс, которому требуется это автоматическое отслеживание обновлений.

public class SomeDTO : IUpdateTracked
{
  // IUpdateTracked implementation as well as other methods for SomeDTO
}

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

void SubmitToWorkflow(object o, User u)
{
  IUpdateTracked updateTracked = o as IUpdateTracked;
  if (updateTracked != null)
  {
     updateTracked.LastUpdated = DateTime.UtcNow;
     updateTracked.UpdatedByUser = u.UserID;
  }
  // ...
  • Мы можем заметить, что вариант void SubmitToWorkflow(IUpdateTracked updateTracked, User u) будет гарантировать безопасность типов, однако в этих обстоятельствах это не кажется уместным.

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

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

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

// Using an interface to bridge properties
public class LegacyDTO : IUpdateTracked
{
    public int LegacyUserID { get; set; }
    public DateTime LastSaved { get; set; }

    public int UpdatedByUser
    {
        get { return LegacyUserID; }
        set { LegacyUserID = value; }
    }
    public DateTime LastUpdated
    {
        get { return LastSaved; }
        set { LastSaved = value; }
    }
}

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

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

// Explicit implementation of an interface
public class YetAnotherObject : IUpdatable
{
    int IUpdatable.UpdatedByUser
    { ... }
    DateTime IUpdatable.LastUpdated
    { ... }

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

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

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

// Decouples the caller and the code as both
// operate only on IList, and are free to swap
// out the concrete collection.
public IList<T> FindDuplicates( IList<T> list )
{
    var duplicates = new List<T>()
    // TODO - write some code to detect duplicate items
    return duplicates;
}

Предупреждение о версии

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

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

Интерфейсы против абстрактных (базовых) классов

Абстрактные классы могут обеспечивать реализацию, а интерфейсы - нет. Абстрактные классы в некоторой степени более гибки в аспекте управления версиями, если вы следуете некоторым рекомендациям, таким как шаблон NVPI (Non-Virtual Public Interface).

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

Внедрение зависимости

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

class AnnualRaiseAdjuster
   : ISalaryAdjuster
{
   AnnualRaiseAdjuster(IPayGradeDetermination payGradeDetermination) { ...  }

   void AdjustSalary(Staff s)
   {
      var payGrade = payGradeDetermination.Determine(s);
      s.Salary = s.Salary * 1.01 + payGrade.Bonus;
   }
}

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

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

Ответ 6

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

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

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

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

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

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

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

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

Итак, в вашем MessageController вы можете иметь метод, называемый следующим образом:

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

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

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

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

Почему и как?

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

Вы видите суть?

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

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

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

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

ArrayList ar = (ArrayList)SomeIList;

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

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

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

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

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

Надеюсь, это даст вам подсказку. Другие пользователи могут опубликовать другие хорошие примеры. Удачи! и Объявите силу INterfaces.

предупреждение: не все хорошо об интерфейсах, есть проблемы с ними, пуристы ООП начнут войну с этим. Я останусь в стороне. Один из недостатков Interfce (по крайней мере, в .NET 2.0) заключается в том, что вы не можете иметь членов PRIVATE или защищены, он должен быть общедоступным. Это имеет смысл, но иногда вы хотите, чтобы вы просто объявляли вещи частными или защищенными.

Ответ 7

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

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

Возможность решения сложной вещи одним словом весьма полезна.

Ответ 8

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

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



    }

}

Ответ 9

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

Некоторые затронули это выше, полиморфизм просто означает, что данный класс может принимать разные "формы". Значение, если у нас есть два класса: "Собака" и "Кошка", и оба реализуют интерфейс "INeedFreshFoodAndWater" (hehe) - ваш код может сделать что-то вроде этого (псевдокод):

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

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

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

Ответ 10

ОК, так что это об абстрактных классах против интерфейсов...

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

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

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

Ответ 11

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

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

Ответ 12

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

Ответ 13

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

Ответ 14

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

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

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

Ответ 15

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

Скажите, что у вас есть:

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

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

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

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

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

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

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

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

Ответ 16

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

Ответ 17

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

Ответ 18

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

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

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

Ответ 19

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

Давайте посмотрим, поможет ли код:

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();
}

Ответ 20

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

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

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

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

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

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

Ответ 21

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

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

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

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

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

Ответ 22

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

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

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

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

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

Ответ 23

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

Ответ 24

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

Ответ 25

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

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

Ответ 26

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

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

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

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

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

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