Как я узнаю, когда создавать интерфейс? - PullRequest
188 голосов
/ 14 января 2009

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

Я часто читаю о них, но мне кажется, что я не могу их понять.

Я читал примеры, такие как: базовый класс Animal, с интерфейсом IAnimal для таких вещей, как 'Walk', 'Run', 'GetLegs' и т. Д., Но я никогда не работал над чем-то и чувствовал, что "Эй, я должен используйте интерфейс здесь! "

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

Ответы [ 24 ]

5 голосов
/ 14 января 2009

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

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

  1. В нашей модели плагинов мы загружаем плагины по интерфейсу и предоставляем этот интерфейс модулям записи плагинов для соответствия.

  2. В нашей системе обмена сообщениями между машинами все классы сообщений реализуют определенный интерфейс и «развертываются» с помощью интерфейса.

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

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

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

4 голосов
/ 14 января 2009

Я использовал интерфейсы время от времени, и вот мое последнее использование (имена обобщены):

У меня есть несколько пользовательских элементов управления в WinForm, которые должны сохранять данные в моем бизнес-объекте. Один из подходов заключается в том, чтобы вызывать каждый элемент управления отдельно:

myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);

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

Я изменил свои элементы управления, чтобы реализовать интерфейс ISaveable, который имеет метод SaveToBusinessObject (...), поэтому теперь мой метод «Сохранить данные» просто перебирает элементы управления, и если он находит тот, который является ISaveable, он вызывает SaveToBusinessObject. Поэтому теперь, когда нужен новый элемент управления, все, что нужно сделать, это реализовать ISaveable в этом объекте (и никогда не трогать другой класс).

foreach(Control c in Controls)
{
  ISaveable s = c as ISaveable;

  if( s != null )
      s.SaveToBusinessObject(myBusinessObject);
}

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

РЕДАКТИРОВАТЬ: Еще одно преимущество - конкретность в действиях. Как и в моем примере, все, что я хочу сделать, это сохранить данные; Мне все равно, что это за элемент управления или если он может делать что-то еще - я просто хочу знать, смогу ли я сохранить данные в элементе управления. Это делает мой код сохранения довольно понятным - нет никаких проверок, чтобы увидеть, текстовый он, числовой, логический или какой-либо еще, потому что все это обрабатывает пользовательский элемент управления.

4 голосов
/ 14 января 2009

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

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

Другим практическим примером является интерфейс ActionListener (или Runnable). Вы бы реализовали их, когда вам нужно отслеживать конкретное событие. Поэтому вам необходимо предоставить реализацию метода actionPerformed(Event e) в вашем классе (или подклассе). Аналогично, для интерфейса Runnable вы предоставляете реализацию для метода public void run().

Кроме того, эти интерфейсы могут быть реализованы любым количеством классов.

Другим примером использования интерфейсов (в Java) является реализация множественного наследования, предлагаемого в C ++.

3 голосов
/ 26 мая 2017

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

Модель до интерфейсов

enter image description here

class Mosquito {
    void flyAroundYourHead(){}
}

class Neighbour{
    void startScreaming(){}
}

class LampJustOutsideYourWindow(){
    void shineJustThroughYourWindow() {}
}

Как вы ясно видите, многие "вещи" могут раздражать, когда вы пытаетесь уснуть.

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

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

class TestAnnoyingThings{
    void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
         if(mosquito != null){
             mosquito.flyAroundYourHead();
         }
         if(neighbour!= null){
             neighbour.startScreaming();
         }
         if(lamp!= null){
             lamp.shineJustThroughYourWindow();
         }
    }
}

Модель с интерфейсами

Чтобы преодолеть эту проблему, мы можем ввести интерфейс enter image description here

interface Annoying{
   public void annoy();

}

И реализовать это внутри классов

class Mosquito implements Annoying {
    void flyAroundYourHead(){}

    void annoy(){
        flyAroundYourHead();
    }
}

class Neighbour implements Annoying{
    void startScreaming(){}

    void annoy(){
        startScreaming();
    }
}

class LampJustOutsideYourWindow implements Annoying{
    void shineJustThroughYourWindow() {}

    void annoy(){
        shineJustThroughYourWindow();
    }
}

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

Что значительно упростит использование этих классов

class TestAnnoyingThings{
    void testAnnoyingThinks(Annoying annoying){
        annoying.annoy();
    }
}
2 голосов
/ 14 января 2009

Самый простой пример - что-то вроде платежных процессоров. (Paypal, PDS и т. Д.).

Допустим, вы создали интерфейс IPaymentProcessor с методами ProcessACH и ProcessCreditCard.

Теперь вы можете реализовать конкретную реализацию Paypal. Чтобы эти методы вызывали специфические функции PayPal.

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

2 голосов
/ 15 июля 2013

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

У нас может быть интерфейс Gun, который определяет функцию shoot().

Нам нужны разные подклассы класса Gun, а именно ShotGun Sniper и т. Д.

ShotGun implements Gun{
    public void shoot(){
       \\shotgun implementation of shoot.
    } 
}

Sniper implements Gun{
    public void shoot(){
       \\sniper implementation of shoot.
    } 
}

Класс стрелка

Стрелок имеет все оружие в своей броне. Давайте создадим List для его представления.

List<Gun> listOfGuns = new ArrayList<Gun>();

Стрелок циклически перебирает оружие, используя функцию switchGun()

public void switchGun(){
    //code to cycle through the guns from the list of guns.
    currentGun = //the next gun in the list.
}

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

public void fire(){
    currentGun.shoot();
}

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

Заключение

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

например Функция fire() из класса Shooter ожидает, что оружие (Sniper, ShotGun) выполнит функцию shoot(). Так что если мы переключим пистолет и стреляем.

shooter.switchGun();
shooter.fire();

Мы изменили поведение функции fire().

2 голосов
/ 15 января 2009

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

http://www.nmock.org/

2 голосов
/ 15 января 2009

Если вы просматриваете сборки .NET Framework и углубляетесь в базовые классы для любого из стандартных объектов, вы заметите много интерфейсов (членов, названных ISomeName).

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

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

КЛАССИЧЕСКИЙ ПРИМЕР: без реализации интерфейса IDisposable вы не можете использовать конструкцию ключевого слова using () в C #, поскольку для этого требуется, чтобы любой объект, указанный в качестве параметра, мог быть неявно приведен к IDisposable.

КОМПЛЕКСНЫЙ ПРИМЕР: Более сложным примером будет класс System.ComponentModel.Component. Этот класс реализует как IDisposable, так и IComponent. Большинство, если не все, объекты .NET, с которыми связан визуальный конструктор, реализуют IComponent, чтобы среда IDE могла взаимодействовать с компонентом.

ВЫВОД: По мере знакомства с .NET Framework, первое, что вы сделаете, столкнувшись с новым классом в Обозревателе объектов или в инструменте .NET Reflector (бесплатно) (http://www.red -gate.com / products /). отражатель / ) должен проверить, какой класс он наследует, а также интерфейсы, которые он реализует. .NET Reflector даже лучше, чем Object Browser, потому что он позволяет вам также видеть классы Derived. Это позволяет вам узнать обо всех объектах, которые являются производными от определенного класса, тем самым потенциально узнавая о существующей функциональности фреймворка. Это особенно важно, когда в .NET Framework добавлены обновленные или новые пространства имен.

1 голос
/ 14 января 2009

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

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

(Метод Dispose () также вызывается с помощью языковой конструкции для VB.NET и C # , которая работает только на IDisposable s)

Имейте в виду, что вы можете проверить, реализует ли объект определенный интерфейс, используя такие конструкции, как TypeOf ... Is (VB.NET), is (C #), instanceof (Java) и т. Д. ...

1 голос
/ 29 ноября 2012

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

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

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

Также см. Этот связанный вопрос о кодировании интерфейса .

...