Почему я не могу понять интерфейсы? - PullRequest
50 голосов
/ 23 сентября 2008

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

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

Ответы [ 26 ]

1 голос
/ 23 сентября 2008

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

1 голос
/ 23 сентября 2008

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

1 голос
/ 23 сентября 2008

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

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

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

1 голос
/ 23 сентября 2008

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

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

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. У меня гораздо больше шансов реализовать класс хотя бы с одним методом, содержащим осмысленное поведение, и использовать абстрактные методы для поддержки шаблонных методов, вызываемых этим методом. Тогда абстрактный класс является базовой реализацией поведения, которым могут пользоваться все конкретные подклассы без необходимости переопределения.

0 голосов
/ 05 августа 2012

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

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

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

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

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

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

0 голосов
/ 23 сентября 2008

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

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

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