Должен ли каждый класс, который я пишу, придерживаться интерфейса?

10

Я пишу игру на Typescript и решил, что буду пытаться придерживаться идеи « программирования на основе интерфейса », где вы пишете код, основанный на интерфейсе, а не на реализации объекта.

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

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

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

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

Ответы:

6

Причина наличия интерфейсов в том, что это упрощает полиморфизм. Это означает, что вы можете отправлять экземпляры по контракту, а не знать их фактическую реализацию. Например, вы можете отправить «Reader» методу, чтобы такой вызываемый метод мог использовать его метод «read ()». Объявив интерфейс «Читатель», вы можете заставить любой объект соответствовать этому контракту, реализуя методы, которые он определяет. Таким образом, любой вызывающий может предположить, что определенные методы будут существовать, даже если объекты, лежащие в основе контракта, могут быть совершенно другими.

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

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

Если у вас есть только ОДНА цепочка наследования (baseclass-> subclass) или только база, или вам не нужно фактически передавать связанные объекты кому-либо еще или использовать их в общем, то вы не добавите реализации интерфейса, так как это понятие тогда бесполезный.

Ричард Тирегрим
источник
Я бы также добавил, что интерфейсы должны моделировать абстракции, поэтому не создавайте их, просто поднимая все открытые члены класса. Подумайте о том, что представляет этот класс, и поместите в интерфейс только те вещи, которые должен иметь любой объект этого типа. Вы также можете наматывать создание нескольких интерфейсов (например , Movesи Shootsдля вашего примера бака) для одного класса.
TMN
7

Если вы не уверены, действительно ли вам нужны интерфейсы, то, скорее всего, нет. Это ядро ​​принципа ЯГНИ. Когда вам нужно поменять местами реализацию, вы вводите интерфейс.

JacquesB
источник
6

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

Обычно мы используем «интерфейс» для обозначения «класса Java, определенного с помощью interfaceключевого слова» или «класса C ++ только с общедоступными, чисто виртуальными функциями». Это полезные абстракции, потому что они отделяют интерфейс класса от его реализации.

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

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

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

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

При этом наличие интерфейса для всего, скорее всего, излишне. Если вы видите этот шаблон, вы идете далеко:

interface I {
  int getA()
  int getB()
  ...
  int getY()
  int getZ()
}

class C : I {
  int getA() { ... }
  int getB() { ... }
  ...
  int getY() { ... }
  int getZ() { ... }
}

источник
Еще одна причина для интерфейсов заключается в том, что ваша система тестирования делает это необходимым, что может зависеть от вашего языка и среды тестирования. Надеюсь, вам не нужно делать это, конечно.
Дариен
@Darien: Причина, по которой системе тестирования могут потребоваться интерфейсы, заключается в том, что в тесте фактически используется другая (поддельная) реализация, возвращающая вас к первому пункту, когда интерфейс подходит.
Барт ван Инген Шенау
Некоторые отличные вещи здесь. Просто подумайте: интерфейс - это не что иное, как общедоступные методы и члены данных класса . Хотя это верно для реализации интерфейса , это, конечно, не относится к интерфейсу, который никогда не реализуется, хотя и предоставляется - это будет что-то вроде запаха кода.
Робби Ди
@RobbieDee это верно, например, для Java interface, просто так получается, что все в Java interfaceтакже является его общедоступным интерфейсом (концепция ОО).
Все! Запишите второй и третий предложения ответа. Ламинируйте это. Положи это в свой кошелек. Ссылка часто.
радаробоб
5

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

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

Насмешливые рамки

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

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

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


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

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

Робби Ди
источник
Обратите внимание, что внедрение зависимостей и контейнеры IoC - это две совершенно разные вещи (одна - это принцип проектирования, а другая - один из способов конкретного воплощения этого принципа в жизнь с помощью различных сред). Вы можете использовать DI без использования контейнера IoC.
Сара
@kai "Вы можете использовать DI без контейнера IoC" . Я думаю, что более зрелые магазины развития могут (и делают). Просто нет необходимости загрязнять код для того, что якобы является простой концепцией. Джоэл придерживается аналогичного мнения.
Робби Ди
Ягни очень хитрый. Какой бы ни была философия, в большинстве случаев, на самом деле, YAGNI используется как оправдание для срезания углов. Избегать сцепления следует воспринимать более серьезно. Очень печально видеть, что на многих встречах с разработчиками разработчик называет себя заблокированным, потому что кто-то еще не закончил свою работу. Почему блокировка другого разработчика экономит время? Я предлагаю использовать интерфейс, чтобы избежать связи. Конечно, не нужно использовать интерфейс для классов модели.
Ripal Barot