Интерфейс против Базового класса - PullRequest
743 голосов
/ 11 сентября 2008

Когда я должен использовать интерфейс и когда я должен использовать базовый класс?

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

Если у меня класс собак и кошек. Почему я хотел бы реализовать IPet вместо PetBase? Я могу понять наличие интерфейсов для ISheds или IBarks (IMakesNoise?), Потому что они могут быть размещены на основе питомца за питомцем, но я не понимаю, какой использовать для обычного питомца.

Ответы [ 39 ]

1 голос
/ 15 марта 2011

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

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

1 голос
/ 05 января 2016

Когда я должен использовать интерфейс и когда я должен использовать базовый класс?

Вы должны использовать интерфейс, если

  1. У вас есть чистые abstract методы и у вас нет неабстрактных методов
  2. У вас нет реализации по умолчанию методов non abstract (за исключением языка Java 8, где методы интерфейса обеспечивают реализацию по умолчанию)
  3. Если вы используете Java 8, теперь интерфейс предоставит реализацию по умолчанию для некоторых неабстрактных методов. Это сделает interface более пригодным для использования по сравнению с abstract классами.

Загляните в этот SE вопрос для более подробной информации.

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

Да. Это лучше и чище. Даже если у вас есть базовый класс с некоторыми абстрактными методами, давайте базовый класс расширяет методы abstract через интерфейс. Вы можете изменить интерфейс в будущем без изменения базового класса.

Пример в java:

abstract class PetBase implements IPet {
// Add all abstract methods in IPet interface and keep base class clean. 
   Base class will contain only non abstract methods and static methods.
}

Если у меня класс собак и кошек. Почему я хотел бы реализовать IPet вместо PetBase? Я могу понять наличие интерфейсов для ISheds или IBarks (IMakesNoise?), Потому что они могут быть размещены на основе питомца за питомцем, но я не понимаю, какой использовать для обычного питомца.

Я предпочитаю, чтобы базовый класс реализовывал интерфейс.

 abstract class PetBase implements IPet {
 // Add all abstract methods in IPet
 }

 /*If ISheds,IBarks is common for Pets, your PetBase can implement ISheds,IBarks. 
  Respective implementations of PetBase can change the behaviour in their concrete classes*/

 abstract class PetBase implements IPet,ISheds,IBarks {
 // Add all abstract methods in respective interfaces
 }

Преимущества:

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

  2. Вы можете обеспечить неизменность базового класса через интерфейс. Взгляните на эту статью

Обратитесь к этому вопросу SE для получения более подробной информации:

Как мне объяснить разницу между интерфейсом и абстрактным классом?

1 голос
/ 23 января 2015

Составьте список вещей, которые ваши объекты должны быть, иметь или делать, и вещей, которые ваши объекты могут (или могут ) иметь, иметь или делай. Must обозначает ваши базовые типы, а can обозначает ваши интерфейсы.

Например, ваш PetBase должен дышать, а ваш IPet может DoTricks.

Анализ вашей проблемной области поможет вам определить точную иерархическую структуру.

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

Вам следует использовать базовый класс, если другие разработчики не имеют никаких оснований желать использовать собственный базовый класс в дополнение к членам вашего типа и , которые вы предвидите, проблемы с версиями (см. http://haacked.com/archive/2008/02/21/versioning-issues-with-abstract-base-classes-and-interfaces.aspx).

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

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

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

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

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

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

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

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

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

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

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

Стиль IPet / PetBase предполагает, что у вас есть несколько реализаций, таким образом увеличивая значение PetBase, поскольку это упрощает реализацию. Однако, если у вас есть обратное или сочетание двух, где у вас есть несколько клиентов, предоставление помощи класса в использовании интерфейса может снизить стоимость, упрощая использование интерфейса.

0 голосов
/ 30 августа 2014

Спасибо за ответ Джону Лимджапу , но я хочу добавить некоторое объяснение концепции интерфейса и абстрактных базовых классов

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

Адаптировано из Pro C # 5.0 и .NET 4.5 Framework book.

Тип интерфейса может показаться очень похожим на абстрактный базовый класс. Отзыв что когда класс помечен как абстрактный, он может определить любое количество абстрактных членов для полиморфный интерфейс для всех производных типов. Тем не менее, даже когда класс определяет набор абстрактных члены, также можно свободно определять любое количество конструкторов, полевых данных, неабстрактных членов (с реализация) и так далее. Интерфейсы, с другой стороны, содержат только абстрактные определения членов. Полиморфный интерфейс, установленный абстрактным родительским классом, страдает одним существенным ограничением в том, что только производные типы поддерживают члены, определенные абстрактным родителем. Однако в большем В программных системах очень распространено создание нескольких иерархий классов, у которых нет общего родителя. за пределы System.Object. Учитывая, что абстрактные члены в абстрактном базовом классе применяются только к производным типы, у нас нет способа настроить типы в разных иерархиях для поддержки одного и того же полиморфного интерфейс. В качестве примера предположим, что вы определили следующий абстрактный класс:

public abstract class CloneableType
{
// Only derived types can support this
// "polymorphic interface." Classes in other
// hierarchies have no access to this abstract
// member.
   public abstract object Clone();
}

Учитывая это определение, только члены, расширяющие CloneableType, могут поддерживать Clone (). метод. Если вы создадите новый набор классов, которые не расширяют этот базовый класс, вы не сможете получить этот полиморфный интерфейс. Также вы можете вспомнить, что C # не поддерживает множественное наследование классов. Поэтому, если вы хотите создать MiniVan, который является автомобилем и является CloneableType, вы не можете сделать это:

// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}

Как вы уже догадались, на помощь приходят типы интерфейсов. После того, как интерфейс был определен, он может быть реализованным любым классом или структурой, в любой иерархии, в любом пространстве имен или любой сборке (написано на любом языке программирования .NET). Как видите, интерфейсы очень полиморфны. Рассмотрим стандартный интерфейс .NET с именем ICloneable, определенный в пространстве имен System. это Интерфейс определяет один метод с именем Clone ():

public interface ICloneable
{
object Clone();
}
0 голосов
/ 11 сентября 2008

Есть и другие преимущества наследования - например, способность переменной содержать объект родительского или унаследованного класса (без необходимости объявлять его как нечто общее, например, «Объект»). ,

Например, в .NET WinForms большинство компонентов пользовательского интерфейса происходит от System.Windows.Forms.Control, поэтому объявленная переменная может «содержать» практически любой элемент пользовательского интерфейса - будь то кнопка, ListView или что-то еще. есть ты Теперь, конечно, у вас не будет доступа ко всем свойствам или методам элемента, но у вас будут все основные вещи - и это может быть полезно.

...