Как я узнаю, когда создавать интерфейс?

197

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

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

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

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

user53885
источник
1
Вы должны проверить stackoverflow.com/q/8531292/1055241
gprathour

Ответы:

151

это решает эту конкретную проблему:

у вас есть a, b, c, d 4 разных типов. по всему коду у вас есть что-то вроде:

a.Process();
b.Process();
c.Process();
d.Process();

почему бы им не реализовать IProcessable, а затем сделать

List<IProcessable> list;

foreach(IProcessable p in list)
    p.Process();

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


Еще одна конкретная проблема:

Вы когда-нибудь смотрели на System.Linq.Enumerable? Он определяет тонну методов расширения, которые работают с любым типом, который реализует IEnumerable. Поскольку все, что реализует IEnumerable, в основном говорит: «Я поддерживаю итерацию в неупорядоченном шаблоне типа foreach», вы можете определить сложные варианты поведения (Count, Max, Where, Select и т. Д.) Для любого перечислимого типа.

Джимми
источник
2
Это помогает. В чем преимущество наличия интерфейса вместо того, чтобы у всех типов была реализация для метода Process ()?
user53885
Вы не сможете использовать одну и ту же переменную p, если они не являются подклассами одного и того же базового типа или не реализуют интерфейс.
Карл
Вам не нужно разыгрывать, в отличие от 50 различных классов с методом Process. C # не использует «типизацию», потому что A имеет Process (), а B имеет Process (), не означает, что существует какой-либо универсальный способ вызова. Вам нужен интерфейс для этого.
user7116
право. Я просто изменил «var» на «IProcessable», чтобы сделать пример более понятным.
Джимми
2
@Rogerio: я пытался быть универсальным. Дело не в том, что «когда у вас есть вещи, которые имеют функцию Process ()», это «когда у вас есть вещи, которые имеют общий набор методов». пример может быть легко изменен наforeach(IMyCompanyWidgetFrobber a in list) a.Frob(widget, context);
Джимми
134

Мне очень нравится ответ Джимми, но я чувствую, что должен что-то добавить к нему. Ключом ко всему является «умение» в IProcess, способное, Он указывает на возможность (или свойство, но означает «внутреннее качество», а не в смысле свойств C #) объекта, реализующего интерфейс. IAnimal, вероятно, не является хорошим примером для интерфейса, но IWalkable может быть хорошим интерфейсом, если ваша система имеет много возможностей для работы. У вас могут быть классы, полученные от животных, такие как собака, корова, рыба, змея. Первые два, вероятно, реализуют IWalkable, последние два не ходят, поэтому они не будут. Теперь вы спрашиваете: «Почему бы просто не иметь другого суперкласса, WalkingAnimal, от которого происходят собаки и коровы?». Ответ - когда у вас есть что-то полностью за пределами дерева наследования, которое также может ходить, например, робот. Робот будет реализовывать IWalkable, но, вероятно, не будет производным от Animal. Если вы хотите список вещей, которые могут ходить,

Теперь замените IWalkable чем-то более программным, таким как IPersistable, и аналогия станет намного ближе к тому, что вы увидите в реальной программе.

rmeador
источник
9
Мне нравится то, что вы придумали - «Это указывает на возможность». Интерфейсы действительно необходимы, когда нужно определить возможности, потому что основы должны придерживаться своего «базового» класса.
Рамиз Уддин
72

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

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

Элемент
источник
8
Первый называется полиморфизмом. Второй - змеиное масло - если подкласс не является базовым классом (нет нарушения принципа подстановки Лискова), вы должны отдавать предпочтение композиции над наследованием.
Арнис Лапса
@ArnisLapsa Я не совсем понимаю, что вы подразумеваете под "если sublcass не является базовым классом". Когда подкласс не будет "быть" базовым классом? (как в isключевое слово)
Marc.2377
32

Думайте об интерфейсе как о контракте. Это способ сказать: «Эти классы должны следовать этим правилам».

Таким образом, в примере с IAnimal это способ сказать: «Я ДОЛЖЕН иметь возможность вызывать Run, Walk и т. Д. Для классов, которые реализуют IAnimal».

Почему это полезно? Возможно, вы захотите создать функцию, основанную на том факте, что вы должны иметь возможность вызывать Run и Walk, например, для объекта. Вы могли бы иметь следующее:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

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

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

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

В этом связанном вопросе также есть хорошая дискуссия .

Larsenal
источник
1
Не забывайте о независимости реализации. Интерфейс позволяет не обращать внимания на то, как реализована основная функция, просто он делает то, что говорит интерфейс.
Мэтью Брубейкер
19

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

Я всегда привожу пример, если у вас есть класс Sailboat и класс Viper. Они наследуют класс Boat и класс Car соответственно. Теперь скажите, что вам нужно перебрать все эти объекты и вызвать их Drive()метод. Хотя вы могли бы написать некоторый код, подобный следующему:

if(myObject is Boat)
    ((Boat)myObject).Drive()
else
    if (myObject is Car)
        ((Car)myObject).Drive()

Было бы намного проще написать:

((IDrivable)myObject).Drive()
Спенсер Рупорт
источник
16

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

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

Карл
источник
16

Мне нравится армейская аналогия.

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

UML

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

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

Интерфейсы - это программные конструкции, которые помогают достичь полиморфизма.

Необходимость абстрагировать детали для достижения простоты - это ответ на Ваш вопрос.

Полиморфизм , который этимологически означает «множество форм», - это способность обрабатывать объект любого подкласса базового класса, как если бы он был объектом базового класса. Следовательно, базовый класс имеет много форм: сам базовый класс и любые его подклассы.

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

Арнис Лапса
источник
14

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

Есть много других причин, которые я нашел, чтобы укрепить хорошую практику рефакторинга для интерфейсов, но именно юнит-тестирование / насмешка были тем, что обеспечило первый «ага момент» практического опыта.

РЕДАКТИРОВАТЬ : Чтобы уточнить, с модульным тестированием и макетом у меня всегда есть две реализации - реальная, конкретная реализация и альтернативная реализация макета, используемая в тестировании. Когда у вас есть две реализации, значение интерфейса становится очевидным - разберитесь с ним с точки зрения интерфейса, чтобы вы могли заменить реализацию в любое время. В этом случае я заменяю его фиктивным интерфейсом. Я знаю, что могу сделать это без реального интерфейса, если мой класс построен правильно, но использование реального интерфейса усиливает это и делает его чище (понятнее читателю). Без этого стимула я не думаю, что оценил бы ценность интерфейсов, поскольку большинство моих классов когда-либо имели единственную конкретную реализацию.

tvanfosson
источник
Правильно по неправильным причинам. В вашем случае - вы получаете так называемые «интерфейсы заголовков» и добавляете сложность избыточным весам, достигая простоты.
Арнис Лапса
@Arnis - модульное тестирование - «неправильная причина», легкая насмешка над классами для удаления зависимостей в тестах - «неправильная причина». Извините, но я не согласен.
tvanfosson
1
тесты должны косвенно влиять на дизайн , предоставляя обратную связь, если код является или не подлежит тестированию. добавление точек расширяемости для улучшения тестируемости само по себе похоже на мошенничество. Я думаю, что Марк Симан лучше всего резюмирует bit.ly/esi8Wp
Арнис Лапса
1
@Arnis - так ты вообще используешь насмешки в своих юнит-тестах? Если нет, то как удалить зависимость от зависимостей. Вы вообще используете DI? Модульное тестирование заставило меня использовать насмешки и DI; издевательства и DI доказали ценность хорошей практики использования интерфейсов для определения контрактов таким образом, которого никогда не могло достичь ни одно академическое понимание. Из-за моего принятия TDD мой код гораздо менее связан, чем был бы в противном случае. Я думаю, что это хорошо.
tvanfosson
просто сказать, что разложение, которое не происходит вдоль так называемых естественных соединений, приводит к низкой когезии и ненужной сложности.
Арнис Лапса
10

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

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

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

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

Примеры от природы: я не слишком увлечен примерами eats (), makeSound (), move () и т. Д. Они действительно описывают поведение, которое является правильным, но они не описывают взаимодействия и то, как они включены . Очевидные примеры интерфейсов, которые обеспечивают взаимодействие в природе, связаны с размножением, например, цветок обеспечивает определенный интерфейс для пчелы, так что опыление может иметь место.


источник
6

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

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

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

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

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

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

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

Да, это Джейк.
источник
5

Пример кода (комбинация Эндрю с моим дополнительным в интерфейсе « что такое цель» ), который также объясняет, почему интерфейс вместо абстрактного класса в языках без поддержки множественного наследования (c # и Ява):

interface ILogger
{
    void Log();
}
class FileLogger : ILogger
{
    public void Log() { }
}
class DataBaseLogger : ILogger
{
    public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
    public void Log() { }
}

Обратите внимание, что FileLogger и DataBaseLogger не нуждаются в интерфейсе (это может быть абстрактный базовый класс Logger). Но учтите, что вы обязаны использовать сторонний регистратор, который заставляет вас использовать базовый класс (допустим, он предоставляет защищенные методы, которые вам нужно использовать). Поскольку язык не поддерживает множественное наследование, вы не сможете использовать подход абстрактного базового класса.

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

eglasius
источник
4

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

У меня есть несколько пользовательских элементов управления в 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

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

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

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

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

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

эпитафия
источник
3
Пожалуйста, Бог заставит их перестать говорить такие вещи, как множественное наследование в отношении интерфейсов. Вы НЕ наследуете интерфейс в классе. Вы это ОСУЩЕСТВЛЯЕТЕ .
Андрей Ринея
4

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

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

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

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

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

Чтобы преодолеть эту проблему, мы можем ввести интерфейсвведите описание изображения здесь

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();
    }
}
Марчин Шимчак
источник
Хорошо, но не нужно, Neighbourа LampJustOutsideYourWindowтакже нужно реализовать Annoying?
Stardust
Да, спасибо за указание на это. Я внес изменения с этим изменением
Марчин Шимчак
2

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

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

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

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

прививка
источник
2

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

http://www.nmock.org/

Jobo
источник
2

Если вы просматриваете сборки .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 добавлены обновленные или новые пространства имен.

EnocNRoll - AnandaGopal Pardue
источник
2

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

У нас может быть интерфейс, 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.
}

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

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

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

Вывод

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

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

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

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

сортировщик
источник
1

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

Мэтью Брубейкер
источник
1

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

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

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

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

Powerlord
источник
1

Как, возможно, уже ответили несколько человек, интерфейсы могут использоваться для обеспечения определенных поведений между классами, которые не будут реализовывать эти поведения одинаково. Итак, реализуя интерфейс, вы говорите, что ваш класс имеет поведение интерфейса. Интерфейс IAnimal не будет типичным интерфейсом, потому что классы Dog, Cat, Bird и т. Д. Являются типами животных и, вероятно, должны расширять его, что является случаем наследования. Вместо этого интерфейс в этом случае будет больше похож на поведение животных, например, IRunnable, IFlyable, ITrainable и т. Д.

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

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

Я бы рекомендовал прочитать главу об интерфейсах в Java Design от Coad, Mayfield и Kern. Они объясняют это немного лучше, чем обычный вводный текст. Если вы не используете Java, вы можете просто прочитать начало главы, которая в основном представляет собой концепцию.

Райан Темза
источник
1

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

Здесь, как обычно, есть компромисс: гибкость по сравнению с ремонтопригодностью. Какой из них важнее? Там нет ответов - это зависит от проекта. Но только помните, что каждое программное обеспечение должно быть поддержано ...

Поэтому мой совет: не используйте интерфейсы, пока они вам действительно не понадобятся. (В Visual Studio вы можете извлечь интерфейс из существующего класса за 2 секунды, поэтому не спешите.)

Сказав это, когда вам нужно создать интерфейс?

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

И это работает: о)

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

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

С уважением, Сильвен.

Сильвен Родриг
источник
1

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

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

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

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

Fuhrmanator
источник
1

Есть так много целей для использования интерфейса.

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

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

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

Например, IUser, IOrder, IOrderItem

public interface IUser()
{

void AddUser(string name ,string fname);

}

// Same for IOrder and IOrderItem
//


public class  BusinessLayer: IUser, IOrder, IOrderItem

{    
    public void AddUser(string name ,string fname)
    {
        // Do stuffs here.
    }

    // All methods from all interfaces must be implemented.

}

Если вы хотите добавить только пользователя, сделайте так:

IUser user = new (IUser)BusinessLayer();

// It will load  all methods into memory which are declared in the IUser interface.

user.AddUser();
Marc.2377
источник