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

Когда мне следует использовать интерфейс, а когда - базовый класс?

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

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

Я думаю, вы должны принять во внимание одну вещь - интерфейсы могут иметь несколько ограничений, о которых вы можете не знать до самых поздних стадий. Например, с .NET вы не можете сериализовать переменную-член интерфейса, поэтому, если у вас есть класс Zoo и массив переменных-членов IAnimals, вы не сможете сериализовать Zoo (а это означает, что написание WebServices или других вещей, требующих сериализации, будет боль).

synhershko 19.07.2009 16:30

Этот вопрос может помочь понять концепцию интерфейсов. stackoverflow.com/q/8531292/1055241

gprathour 07.07.2014 09:30

Мне просто любопытно. Я встретил в CLR через C# следующий отрывок: I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.. Я не могу понять, что имеется в виду в отрывке. Мы можем создать несколько базовых типов и создать производный тип для любого из них, чтобы разработчик мог выбрать базовый тип. Может кто-нибудь объяснить, пожалуйста, что мне не хватает? Я считаю, что это может быть частью этого вопроса. Или я должен опубликовать еще один о конкретном отрывке?

qqqqqqq 06.03.2020 23:18
В PHP
В PHP
В большой кодовой базе с множеством различных компонентов классы, функции и константы могут иметь одинаковые имена. Это может привести к путанице и...
Принцип подстановки Лискова
Принцип подстановки Лискова
Принцип подстановки Лискова (LSP) - это принцип объектно-ориентированного программирования, который гласит, что объекты суперкласса должны иметь...
786
3
165 062
37
Перейти к ответу Данный вопрос помечен как решенный

Ответы 37

Современный стиль - это определение IPet и PetBase.

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

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

есть свой торт и съесть его тоже!

Darren Kopp 11.09.2008 19:24

да да! А с современными IDE он даже напишет для вас шаблонный код (а некоторым языкам это даже не нужно)

Jason Cohen 11.09.2008 19:26

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

Bill Michell 04.11.2008 01:36

Я нахожу это сомнительным: это совет .NET? У вас есть ссылка? В принципе, есть несколько причин, по которым вы должны создать «базовый» класс, обычно делегирование предпочтительнее наследования.

andygavin 04.06.2009 23:31

Например, чтобы создать класс потока в Java, вы можете расширить java.lang.Threadили же и реализовать java.lang.Runnable. Чтобы создать сервер RMI, вы можете либо расширить java.rmi.server.UnicastRemoteObjectили же, реализовав java.rmi.Remote, либо вызвать UnicastRemoteObject.exportObject().

Kevin Panko 20.05.2010 00:07

Здесь нет ничего «современного». Базовый класс и интерфейс с одним и тем же API просто избыточны. Вы можете использовать этот подход в некоторых случаях, но не следует обобщать!

smentek 20.08.2011 01:39

Самый поддерживаемый комментарий ко второму поддерживаемому ответу на самом деле не согласуется с самим ответом, интересно. Я должен сказать, что вижу много примеров сосуществования интерфейса и базового класса. В этом смысле это «современный» способ. Например, в шаблоне MVVM на самом деле есть класс ViewModelBase, который реализует INotifyPropertyChanged. Но когда мой коллега спросил меня, почему у меня базовый класс, вместо того, чтобы реализовывать интерфейс в каждой модели представления, я не знаю, как его убедить

tete 25.09.2012 18:12

Правильный. Это не вопрос ни одного, ни другого. Они существуют для решения двух очень разных проблем. Интерфейсы - это контракты, к которым должен обращаться реализующий класс. В последнее время они нашли поддержку (иногда фанатично) для аспектов IoC и TDD. Абстрактные / базовые классы служат для иерархической группировки общей логики и свойств. Это уменьшает количество дублированного кода, что, в свою очередь, увеличивает ремонтопригодность решения и снижает его подверженность ошибкам.

ComeIn 09.08.2018 14:41

«Avoid именует базовые классы с суффиксом Base, если класс предназначен для использования в общедоступных API». Рекомендации по проектированию каркаса, стр. 174

Yousha Aleayoub 15.05.2020 12:52

В общем, вам следует отдавать предпочтение интерфейсам, а не абстрактным классам. Одна из причин использовать абстрактный класс - это то, что у вас есть общая реализация среди конкретных классов. Конечно, вы все равно должны объявить интерфейс (IPet) и иметь абстрактный класс (PetBase), реализующий этот интерфейс. Используя небольшие отдельные интерфейсы, вы можете использовать кратные для дальнейшего повышения гибкости. Интерфейсы обеспечивают максимальную гибкость и переносимость типов через границы. При передаче ссылок через границы всегда передавайте интерфейс, а не конкретный тип. Это позволяет принимающей стороне определять конкретную реализацию и обеспечивает максимальную гибкость. Это абсолютно верно при программировании в режиме TDD / BDD.

В своей книге «Банда четырех» заявила: «Поскольку наследование раскрывает для подкласса детали реализации его родителя, часто говорят, что« наследование нарушает инкапсуляцию ». Я верю, что это правда.

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

user1228 11.09.2008 20:59

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

Kilhoffer 11.09.2008 21:39

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

goku_da_master 10.07.2012 20:08

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

JAVA 04.11.2014 17:03

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

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

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

Наконец, единственное наследование .NET убивает меня. Наивный пример: допустим, вы создаете пользовательский элемент управления, поэтому наследуете UserControl. Но теперь вы лишены возможности наследования PetBase. Это вынуждает вас реорганизовать, например вместо этого сделать члена класса PetBase.

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

Ну, Джош Блох сказал сам в Эффективная Java 2d:

Предпочитайте интерфейсы абстрактным классам

Некоторые основные моменты:

  • Existing classes can be easily retrofitted to implement a new interface. All you have to do is add the required methods if they don’t yet exist and add an implements clause to the class declaration.

  • Interfaces are ideal for defining mixins. Loosely speaking, a mixin is a type that a class can implement in addition to its “primary type” to declare that it provides some optional behavior. For example, Comparable is a mixin interface that allows a class to declare that its instances are ordered with respect to other mutually comparable objects.

  • Interfaces allow the construction of nonhierarchical type frameworks. Type hierarchies are great for organizing some things, but other things don’t fall neatly into a rigid hierarchy.

  • Interfaces enable safe, powerful functionality enhancements via the wrap- per class idiom. If you use abstract classes to define types, you leave the programmer who wants to add functionality with no alternative but to use inheritance.

Moreover, you can combine the virtues of interfaces and abstract classes by providing an abstract skeletal implementation class to go with each nontrivial interface that you export.

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

PS: Купите книгу. Это намного более подробно.

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

Scott Lawrence 12.09.2008 00:01

у нас есть «Принцип разделения интерфейса». Этот принцип учит нас заботиться о том, как мы пишем наши интерфейсы. Когда мы пишем наши интерфейсы, мы должны заботиться о добавлении только тех методов, которые должны быть там. Если мы добавим методы, которых не должно быть, классы, реализующие интерфейс, также должны будут реализовать эти методы. Например, если мы создадим интерфейс под названием Worker и добавим перерыв на обед для метода, всем рабочим придется его реализовать. Что, если рабочий - робот? В заключение, интерфейсы, содержащие методы, не относящиеся к нему, называются загрязненными или жирными интерфейсами.

Eklavyaa 21.07.2016 06:46

Начиная с Java 8, методы по умолчанию позволяют добавлять новые функции к интерфейсам и обеспечивать обратную совместимость для существующих классов, реализующих этот интерфейс. По умолчанию вызываются методы по умолчанию, если они не переопределены в реализующем классе. Все реализующие классы могут либо переопределять методы по умолчанию, либо вызывать их напрямую с помощью instance.defaultMethod ()

Arpit Rastogi 12.10.2017 11:39

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

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

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

Абстрактные классы - это ярлыки. Есть ли вещи, которые есть у всех производных PetBase, которые можно кодировать один раз и с которыми можно покончить? Если да, то пришло время абстрактного класса.

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

Абстрактные классы могут содержать несколько интерфейсов. Ваш абстрактный класс PetBase может реализовывать IPet (у домашних животных есть хозяева) и IDigestion (домашние животные едят или, по крайней мере, должны). Однако PetBase, вероятно, не будет реализовывать IMammal, поскольку не все домашние животные являются млекопитающими и не все млекопитающие являются домашними животными. Вы можете добавить MammalPetBase, который расширяет PetBase, и добавить IMammal. FishBase может иметь PetBase и добавлять IFish. IFish будет иметь интерфейсы ISwim и IUnderwaterBreather.

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

Ответ принят как подходящий

Возьмем ваш пример классов Dog и Cat и проиллюстрируем использование C#:

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

public abstract class Mammal

Этот базовый класс, вероятно, будет иметь методы по умолчанию, такие как:

  • Подача
  • Приятель

Все это поведение, которое имеет более или менее одинаковую реализацию у обоих видов. Чтобы определить это, вам понадобится:

public class Dog : Mammal
public class Cat : Mammal

Теперь предположим, что есть и другие млекопитающие, которых мы обычно видим в зоопарках:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

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

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

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

Реализация вышеуказанного контракта между кошкой и собакой не будет одинаковой; размещение их реализаций в абстрактном классе для наследования будет плохой идеей.

Теперь определения вашей собаки и кошки должны выглядеть так:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

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

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

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

David Pokluda 11.09.2008 22:34

Интерфейс - это контракт. Вы раскрываете только ту часть контракта, которая требуется службе. Если у вас есть «PettingZoo», вы, конечно же, не хотите раскрывать «Спаривание» пользователю!

Anthony Mastrean 11.09.2008 23:54

@David Touche, хотя я сделал это, чтобы лучше проиллюстрировать, для чего нужен интерфейс и что такое абстрактный класс для его понимания. Собака и кошка не являются жесткими требованиями!

Jon Limjap 12.09.2008 05:13

@Jon Вау, мне нравится ваше решение проблемы контактного зоопарка

smaclell 18.10.2008 06:08

@DaveRook LOL! Отсюда мой определитель «возможно». Feed и Mate выглядят корректно, возможно, Hunt должен быть в другом интерфейсе, таком как IPredator или ICarnivore: D

Jon Limjap 29.01.2014 07:10

Следует отметить, что в интерпретируемых средах JIT (особенно JVM) вызовы виртуальных методов значительно быстрее, чем вызовы методов интерфейса, в зависимости от контекста. Я подчеркиваю «контекст», потому что JVM часто может оптимизировать медленный поиск методов. (например, если список наследников интерфейса, как правило, является экземплярами одного и того же класса. Это, к сожалению, немного затрудняет тестирование.) Если вы пытаетесь оптимизировать что-то, чувствительное к производительности, и только тогда вы должны учитывать это.

Philip Guin 23.02.2014 02:59

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

eclux 09.12.2015 20:48

@eclux Ага, разве это не круто? : D

Jon Limjap 10.12.2015 01:50

Интерфейсы

  • Определяет договор между 2 модулями. Не может иметь никакой реализации.
  • Большинство языков позволяют реализовать несколько интерфейсов
  • Изменение интерфейса - это серьезное изменение. Все реализации необходимо перекомпилировать / модифицировать.
  • Все участники публичны. Реализации должны реализовывать всех членов.
  • Интерфейсы помогают в развязке. Вы можете использовать фреймворки для имитации всего, что скрывается за интерфейсом.
  • Интерфейсы обычно указывают на какое-то поведение
  • Реализации интерфейсов отделены / изолированы друг от друга

Базовые классы

  • Позволяет вам добавить некоторую реализацию дефолт, которую вы получаете бесплатно по производной
  • За исключением C++, вы можете быть производным только от одного класса. Даже если бы мог из нескольких классов, это обычно плохая идея.
  • Изменить базовый класс относительно просто. От выводов ничего особенного делать не нужно
  • Базовые классы могут объявлять защищенные и общедоступные функции, к которым можно получить доступ производными
  • Абстрактные базовые классы не так просто издеваться, как интерфейсы
  • Базовые классы обычно указывают иерархию типов (IS A)
  • Происхождение классов может зависеть от некоторого базового поведения (иметь сложные знания о родительской реализации). Все может быть запутано, если вы измените базовую реализацию для одного человека и сломаете остальные.

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

Gishu 30.12.2010 07:41

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

Кшиштоф Квалина говорит на странице 81:

Over the course of the three versions of the .NET Framework, I have talked about this guideline with quite a few developers on our team. Many of them, including those who initially disagreed with the guidelines, have said that they regret having shipped some API as an interface. I have not heard of even one case in which somebody regretted that they shipped a class.

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

Наследник базового класса должен иметь отношение «есть». Интерфейс представляет собой отношение «реализует». Поэтому используйте базовый класс только тогда, когда ваши наследники будут поддерживать отношения is.

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

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

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

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

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

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

Таким образом, вместо IPet или PetBase вы можете получить Pet с параметром IFurBehavior. Параметр IFurBehavior устанавливается методом CreateDog () PetFactory. Именно этот параметр вызывается для метода shed ().

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

Я рекомендую этот шаблон даже в языках множественного наследования.

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

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

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

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

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

Kilhoffer 12.09.2008 00:09

Хуан,

Мне нравится думать об интерфейсах как о способе характеристики класса. Конкретный класс породы собак, например YorkshireTerrier, может быть потомком родительского класса собак, но он также реализует IFurry, IStubby и IYippieDog. Итак, класс определяет, что это за класс, но интерфейс рассказывает нам о нем.

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

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

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

Так что если вы думаете, как я, вы определенно скажете, что Cat и Dog - это IPettable. Это характеристика, которая подходит им обоим.

Другой момент - должны ли они иметь один и тот же базовый класс? Вопрос в том, нужно ли к ним широко относиться как к одному и тому же. Конечно, они оба Животные, но это соответствует тому, как мы собираемся использовать их вместе.

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

Или они должны быть млекопитающими? Может, нужна какая-то доильная фабрика кросс-животных?

Нужно ли их вообще связывать вместе? Достаточно ли просто знать, что они оба IPettable?

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

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

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

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

Public Inheritance чрезмерно используется в OOD и выражает гораздо больше, чем большинство разработчиков осознают или готовы жить. См. Принцип заменяемости Лискова

Короче говоря, если A "является" B, то A требует не больше, чем B, и обеспечивает не меньше, чем B, для каждого метода, который он предоставляет.

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

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

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

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

Наследование (базовые классы) представляют отношение «есть». Например. собака или кошка "является" домашним животным. Это отношение всегда представляет (одиночный) цель класса (вместе с «принцип единой ответственности»).

Интерфейсы, с другой стороны, представляют дополнительные возможности класса. Я бы назвал это отношением «есть», например, «Foo одноразовый», отсюда и интерфейс IDisposable в C#.

Из всех ответов этот дает наилучшее сочетание краткости без потери ясности.

Matt Wilko 17.04.2014 12:16

Кто-то однажды сказал мне использовать интерфейс, когда есть отношение «есть». Я не уверен, всегда ли это правда; У моего ноутбука есть экран, поэтому должен ли ноутбук использовать IScreen или иметь свойство Screen? Последнее мне кажется более естественным.

Berend 30.10.2014 15:47

@berend смешно, что ты пишешь, потому что экраны реализованы через интерфейсы - VGA, HDMI и т. д.

Konstantin 16.12.2014 23:32

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

Crismogram 11.04.2018 05:32

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

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

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

Существует различие между базовый класс и абстрактные базовые классы (ABC). ABC смешивают интерфейс и реализацию вместе. Абстрактное за пределами компьютерного программирования означает «резюме», то есть «абстрактный == интерфейс». Затем абстрактный базовый класс может описывать как интерфейс, так и пустую, частичную или полную реализацию, которая предназначена для наследования.

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

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

Вот базовое и простое определение интерфейса и базового класса:

  • Базовый класс = наследование объекта.
  • Интерфейс = функциональное наследование.

ваше здоровье

Это хорошо объясняется в этом Статья Java World.

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

Нередко у меня будет класс, реализующий один или несколько интерфейсов.

Абстрактные классы я использую как основу для чего-то еще.

Ниже приводится выдержка из вышеупомянутой статьи Статья JavaWorld.com, автор Тони Синтес, 20.04.01.


Interface vs. abstract class

Choosing interfaces and abstract classes is not an either/or proposition. If you need to change your design, make it an interface. However, you may have abstract classes that provide some default behavior. Abstract classes are excellent candidates inside of application frameworks.

Abstract classes let you define some behaviors; they force your subclasses to provide others. For example, if you have an application framework, an abstract class may provide default services such as event and message handling. Those services allow your application to plug in to your application framework. However, there is some application-specific functionality that only your application can perform. Such functionality might include startup and shutdown tasks, which are often application-dependent. So instead of trying to define that behavior itself, the abstract base class can declare abstract shutdown and startup methods. The base class knows that it needs those methods, but an abstract class lets your class admit that it doesn't know how to perform those actions; it only knows that it must initiate the actions. When it is time to start up, the abstract class can call the startup method. When the base class calls this method, Java calls the method defined by the child class.

Many developers forget that a class that defines an abstract method can call that method as well. Abstract classes are an excellent way to create planned inheritance hierarchies. They're also a good choice for nonleaf classes in class hierarchies.

Class vs. interface

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

For example, the Strategy pattern lets you swap new algorithms and processes into your program without altering the objects that use them. A media player might know how to play CDs, MP3s, and wav files. Of course, you don't want to hardcode those playback algorithms into the player; that will make it difficult to add a new format like AVI. Furthermore, your code will be littered with useless case statements. And to add insult to injury, you will need to update those case statements each time you add a new algorithm. All in all, this is not a very object-oriented way to program.

With the Strategy pattern, you can simply encapsulate the algorithm behind an object. If you do that, you can provide new media plug-ins at any time. Let's call the plug-in class MediaStrategy. That object would have one method: playStream(Stream s). So to add a new algorithm, we simply extend our algorithm class. Now, when the program encounters the new media type, it simply delegates the playing of the stream to our media strategy. Of course, you'll need some plumbing to properly instantiate the algorithm strategies you will need.

This is an excellent place to use an interface. We've used the Strategy pattern, which clearly indicates a place in the design that will change. Thus, you should define the strategy as an interface. You should generally favor interfaces over inheritance when you want an object to have a certain type; in this case, MediaStrategy. Relying on inheritance for type identity is dangerous; it locks you into a particular inheritance hierarchy. Java doesn't allow multiple inheritance, so you can't extend something that gives you a useful implementation or more type identity.

+1. «Опираться на наследование для идентификации типа опасно; оно блокирует вас в определенной иерархии наследования». Это предложение прекрасно описывает причины, по которым я предпочитаю интерфейсы.

Engineer 31.05.2011 18:48

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

Engineer 31.05.2011 18:54

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

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

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Да, этот пример показывает, что при таком подходе много дублирования кода и недостатка элегантности. Но нужно также понимать, что это помогает держать Dog и Cat отделенными от класса Pet (в том смысле, что Dog и Cat не имеют доступа к закрытым членам Pet), и это оставляет место для Dog и Cat, чтобы унаследовать их от чего-то еще - -возможно, класс Mammal.

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

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

Случай использования базовых классов поверх интерфейсов был хорошо объяснен в Руководстве по кодированию Submain .NET:

Base Classes vs. Interfaces An interface type is a partial description of a value, potentially supported by many object types. Use base classes instead of interfaces whenever possible. From a versioning perspective, classes are more flexible than interfaces. With a class, you can ship Version 1.0 and then in Version 2.0 add a new method to the class. As long as the method is not abstract, any existing derived classes continue to function unchanged.

Because interfaces do not support implementation inheritance, the pattern that applies to classes does not apply to interfaces. Adding a method to an interface is equivalent to adding an abstract method to a base class; any class that implements the interface will break because the class does not implement the new method. Interfaces are appropriate in the following situations:

  1. Several unrelated classes want to support the protocol.
  2. These classes already have established base classes (for example, some are user interface (UI) controls, and some are XML Web services).
  3. Aggregation is not appropriate or practicable. In all other situations, class inheritance is a better model.

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

kayleeFrye_onDeck 19.12.2015 03:29

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

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

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

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

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

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

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

Adam Hughes 04.03.2016 21:14

Что касается C#, в некотором смысле интерфейсы и абстрактные классы могут быть взаимозаменяемыми. Однако различия заключаются в следующем: i) интерфейсы не могут реализовывать код; ii) из-за этого интерфейсы не могут вызывать далее стек для подкласса; и iii) только абстрактный класс может быть унаследован от класса, тогда как несколько интерфейсов могут быть реализованы в классе.

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

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

У меня есть примерное практическое правило

Функциональность: скорее всего будет разным во всех частях: Интерфейс.

Абстрактный класс Данные и функциональность, части будут в основном одинаковыми, части разными:.

Фактически работающие данные и функциональность, если их расширить с небольшими изменениями: обыкновенный (бетонный) класс

Данные и функционал, изменений не планируется: обычный (бетонный) класс с модификатором final.

Члены перечисления Данные и, возможно, функциональность: только для чтения:.

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

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

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

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

Тип интерфейса может показаться очень похожим на абстрактный базовый класс. Отзывать что когда класс помечен как абстрактный, он может определять любое количество абстрактных членов для предоставления полиморфный интерфейс для всех производных типов. Однако даже если класс действительно определяет набор абстрактных членов, также можно свободно определять любое количество конструкторов, данных полей, неабстрактных членов (с реализация) и так далее. С другой стороны, интерфейсы содержат только абстрактные определения членов. Полиморфный интерфейс, установленный абстрактным родительским классом, страдает одним серьезным ограничением. в этом только производные типы поддерживают члены, определенные абстрактным родителем. Однако в более крупных программных систем, очень часто разрабатывают несколько иерархий классов, у которых нет общего родительского за пределами 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();
}

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

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

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

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

When should I use an interface and when should I use a base class?

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

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

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

Should it always be an interface if I don't want to actually define a base implementation of the methods?

Да. Так лучше и чище. Даже если у вас есть базовый класс с некоторыми абстрактными методами, пусть базовый класс расширяет методы 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.
}

If I have a Dog and Cat class. Why would I want to implement IPet instead of PetBase? I can understand having interfaces for ISheds or IBarks (IMakesNoise?), because those can be placed on a pet by pet basis, but I don't understand which to use for a generic Pet.

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

 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 для получения более подробной информации:

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

Предпочитайте интерфейсы абстрактным классам

Обоснование, основные моменты, которые следует учитывать [два уже упомянутых здесь]:

  • Интерфейсы более гибкие, потому что класс может реализовывать несколько интерфейсы. Поскольку Java не имеет множественного наследования, использование абстрактные классы не позволяют вашим пользователям использовать любой другой класс иерархия. Коллекции В общем, предпочитайте интерфейсы, когда нет значений по умолчанию реализации или состояние. Java предлагают хорошие примеры это (Карта, Набор и т. д.).
  • У абстрактных классов есть то преимущество, что они позволяют лучше продвигаться вперед совместимость. Когда клиенты используют интерфейс, вы не можете его изменить; если они используют абстрактный класс, вы все равно можете добавить поведение без нарушение существующего кода. Если совместимость вызывает беспокойство, рассмотрите возможность использования абстрактные классы.
  • Даже если у вас есть реализации по умолчанию или внутреннее состояние, рассмотреть возможность предложения интерфейса и его абстрактной реализации. Это поможет клиентам, но при этом предоставит им большую свободу, если желаемый [1] .
    Конечно, эта тема подробно обсуждалась. в другом месте [2,3].

[1] Это, конечно, добавляет больше кода, но если краткость является вашей основной заботой, вам, вероятно, в первую очередь следовало избегать Java!

[2] Джошуа Блох, Эффективная Java, пункты 16–18.

[3] http://www.codeproject.com/KB/ar ...

Я обнаружил, что шаблон Интерфейс> Аннотация> Бетон работает в следующем сценарии использования:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

Абстрактный класс определяет общие атрибуты по умолчанию для конкретных классов, но обеспечивает интерфейс. Например:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

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

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

А затем конкретные классы просто определяют, что они ходят прямо.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

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

В этом случае абстрактное может быть также конкретным; однако абстрактное обозначение помогает подчеркнуть, что этот шаблон используется.

Другие вопросы по теме