Сомневаетесь в использовании интерфейса? - PullRequest
2 голосов
/ 15 февраля 2010

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

У меня есть следующий интерфейс

interface Imammals
          {
           walk();
           eat();
           run();
          }

и у меня есть два класса Человек и Кошка , которые реализуют этот интерфейс.

В любом случае, функциональность методов будет отличаться в обоих классах.

Например: walk (), функциональность отличается, так как кошка использует четыре ноги, а человек использует две ноги

Тогда, зачем мне нужен общий интерфейс, который ТОЛЬКО объявляет эти методы? Мой дизайн здесь неисправен?

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

Но здесь интерфейсы помогают нам просто объединить объявления методов или есть что-то еще внутри?

РЕДАКТИРОВАТЬ: ходьба (), еда (), бег () был изменен на ходьба (), есть (), бегать () и млекопитающие были изменения для имаммалов.

Ответы [ 14 ]

3 голосов
/ 15 февраля 2010

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

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

2 голосов
/ 15 февраля 2010

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

Также вы можете использовать композицию вместо конкретной реализации, если некоторые из ваших объектов имеют общее поведение.

Прочитайте немного о паттерне Стратегии, думаю, это поможет.

2 голосов
/ 15 февраля 2010

Используя интерфейс, вы можете получить следующее:

   public void walkMyAnimal(Animal animal) {
       animal.walk();
   }

без необходимости знать, какое именно животное пропущено.

1 голос
/ 15 февраля 2010

Интерфейс предоставляет контракт. Это не обеспечивает реализацию. Это хорошая практика для взаимодействия ваших классов.

1 голос
/ 15 февраля 2010

Ну, иногда вам захочется сделать что-то с «чем-нибудь способным бегать», не обязательно во время разработки знать, говорите ли вы о человеке, о коте или о чем-то еще. Например, представьте себе функцию mammal raceWinner(mammal m1, mammal m2){...} рассчитать, какое млекопитающее победит в гонке. Чтобы определить, кто победит, возможно, функция должна вызвать m1.running() и m2.running(). Конечно, млекопитающие, в которых мы проезжаем, действительно будут кошками, людьми или чем-то еще, и этот класс обеспечивает фактическую реализацию running(). Но все, что нужно знать raceWinner, - это метод running() с ожидаемой подписью.

Если бы мы только определили running() для cat и human, мы не смогли бы вызвать m1.running() (потому что компилятору не гарантируется, что m1 имеет метод running(), поскольку он только знает, что это m1 реализует mammal). Таким образом, вместо этого нам пришлось бы реализовать raceWinner(human m1, cat m2), а также для двух людей, двух кошек или любой другой пары млекопитающих, которые мы имели в виду, что привело бы к гораздо большей работе с нашей стороны.

1 голос
/ 15 февраля 2010

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

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

1 голос
/ 15 февраля 2010

Одним большим преимуществом интерфейсов является то, что даже в таких языках, как Java и C #, где множественное наследование не разрешено, класс может иметь более одного интерфейса.Что-то может быть как Closable, например, так и List, но не может наследовать от обоих (гипотетических) абстрактных базовых классов AbstractClosable и AbstractList.

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

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

0 голосов
/ 15 февраля 2010

Здесь есть две проблемы, которые часто путают. Унаследованное поведение позволяет одинаково реагировать на различные «команды», например, Man.walk () === Woman.walk (). Полиморфное поведение позволяет реагировать на одну и ту же «команду» по-разному, например, Animal.move () для одного объекта может отличаться для Animal.move () для другого, птица будет летать, а слизняк будет скользить.

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

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

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

0 голосов
/ 15 февраля 2010

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

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

foreach(mammals m in MamalsArrayList){
{
    m.walking();
}

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

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

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

Конечно, у них есть другие применения (которые упоминаются в других ответах), я просто сосредоточился на вашем примере.

0 голосов
/ 15 февраля 2010

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

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

например, вы могли бы иметь:

class Cat : IMammal, IFourLeggedAnimal
{
}

class Human: IMammal, ITwoLeggedAnimal
{
}

Теперь вы можете относиться к обоим из них как к млекопитающим с помощью метода walk (), или вы можете обращаться с ними как с четырьмя или двумя ножками (не обязательно млекопитающими). ​​

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