Почему мы используем интерфейс, потому что он имеет пустое тело и должен переопределяться в дочернем классе [duplicate]

Другой вопрос был назван дубликатом этого:

В C ++ почему результат cout << x отличается от значения, которое показывает отладчик для x ?

x в вопросе - это переменная float.

Одним из примеров может быть

float x = 9.9F;

Отладчик показывает 9.89999962, вывод работы cout - 9.9.

Ответ оказывается, что точность cout по умолчанию для float равна 6, поэтому она округляется до шести десятичных цифры

См. здесь для справки

711
задан 6 revs, 6 users 100% 6 March 2016 в 22:25
поделиться

28 ответов

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

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

public abstract class Mammal

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

  • Feed
  • Mate

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

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

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

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

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

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

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

461
ответ дан 8 revs, 5 users 94% 25 August 2018 в 04:55
поделиться

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

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

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

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

106
ответ дан 2 revs 25 August 2018 в 04:55
поделиться

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

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

приветствия

15
ответ дан 2 revs, 2 users 77% 25 August 2018 в 04:55
поделиться

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

10
ответ дан 2 revs, 2 users 80% 25 August 2018 в 04:55
поделиться

Интерфейсы

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

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

  • Позволяет добавить некоторую реализацию по умолчанию, которую вы получаете бесплатно, по деривации
  • За исключением C ++, вы можете получить только один класс. Даже если это возможно из нескольких классов, это, как правило, плохая идея.
  • Изменение базового класса относительно просто. Деривациям не нужно ничего делать
  • Базовые классы могут объявлять защищенные и общедоступные функции, к которым могут обращаться деривации
  • Абстрактные базовые классы не могут быть легко издевательскими, как интерфейсы
  • Базовые классы обычно указывают иерархию типов (IS A)
  • Отличия классов могут зависеть от какого-либо базового поведения (иметь сложные знания о родительской реализации). Вещи могут быть беспорядочными, если вы внесете изменения в базовую реализацию для одного парня и сломаете других.
57
ответ дан 2 revs, 2 users 97% 25 August 2018 в 04:55
поделиться

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

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

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

93
ответ дан 3 revs 25 August 2018 в 04:55
поделиться

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

The Gang of Four заявили в своей книге «Поскольку наследование предоставляет подкласс для подробностей реализации его родителя, часто говорят, что« наследование прерывает инкапсуляцию », , Я считаю, что это правда.

56
ответ дан 5 revs 25 August 2018 в 04:55
поделиться

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

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

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

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

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

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

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

134
ответ дан 5 revs, 3 users 97% 25 August 2018 в 04:55
поделиться

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

1
ответ дан Aaron Fischer 25 August 2018 в 04:55
поделиться

У меня грубое правило большого пальца

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

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

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

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

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

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

8
ответ дан Akhil Jain 25 August 2018 в 04:55
поделиться

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

2
ответ дан Bill the Lizard 25 August 2018 в 04:55
поделиться

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

2
ответ дан CarneyCode 25 August 2018 в 04:55
поделиться

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

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

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

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

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

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

4
ответ дан Don Edwards 25 August 2018 в 04:55
поделиться

@Joel: Некоторые языки (например, C ++) допускают множественное наследование.

2
ответ дан Einar 25 August 2018 в 04:55
поделиться

Хуан,

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

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

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

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

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

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

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

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

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

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

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

19
ответ дан Flory 25 August 2018 в 04:55
поделиться

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

Krzysztof Cwalina говорит на стр. 81:

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

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

46
ответ дан fryguybob 25 August 2018 в 04:55
поделиться

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

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

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

[1] Он добавляет больше кода, конечно, но если краткость является вашей главной заботой, вы, вероятно, должны во-первых, избегали Java!

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

[3] http: //www.codeproject. ком / KB / ар ...

3
ответ дан Jaimin Patel 25 August 2018 в 04:55
поделиться

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

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

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

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

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

7
ответ дан Jarrett Meyer 25 August 2018 в 04:55
поделиться
4
ответ дан Jason Roell 25 August 2018 в 04:55
поделиться

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

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

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

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

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

12
ответ дан Joe 25 August 2018 в 04:55
поделиться

В этой статье Java World хорошо объясняется

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

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

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

выдержка из вышеупомянутой статьи JavaWorld.com, автор Tony Sintes, 04/20/01


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

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

Абстрактные классы позволяют вам определить некоторые виды поведения; они заставляют ваши подклассы предоставлять другие. Например, если у вас есть инфраструктура приложения, абстрактный класс может предоставлять сервисы по умолчанию, такие как обработка событий и сообщений. Эти службы позволяют вашему приложению подключаться к инфраструктуре приложения. Однако есть некоторые функциональные возможности приложения, которые могут выполнять только ваше приложение. Такие функции могут включать задачи запуска и завершения работы, которые часто зависят от приложения. Поэтому вместо того, чтобы пытаться определить это поведение, абстрактный базовый класс может объявлять абстрактные методы остановки и запуска. Базовый класс знает, что ему нужны эти методы, но абстрактный класс позволяет вашему классу признать, что он не знает, как выполнять эти действия; он знает только, что он должен инициировать действия. Когда пришло время запуска, абстрактный класс может вызвать метод запуска. Когда базовый класс вызывает этот метод, Java вызывает метод, определенный дочерним классом.

Многие разработчики забывают, что класс, который определяет абстрактный метод, также может вызвать этот метод. Абстрактные классы - отличный способ создания запланированных иерархий наследования. Они также являются хорошим выбором для классов nonleaf в иерархиях классов.

Класс против интерфейса

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

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

С шаблоном Strategy вы можете просто инкапсулировать алгоритм, стоящий за объектом. Если вы это сделаете, вы можете предоставить новые плагины для мультимедиа в любое время. Назовем класс плагина MediaStrategy. У этого объекта будет один метод: playStream (Stream s). Поэтому, чтобы добавить новый алгоритм, мы просто расширяем наш класс алгоритмов. Теперь, когда программа встречает новый тип носителя, он просто делегирует воспроизведение потока в нашу медиа-стратегию. Конечно, вам понадобится сантехника, чтобы правильно создать алгоритмы, которые вам понадобятся.

Это отличное место для использования интерфейса. Мы использовали шаблон стратегии, в котором четко указано место в дизайне, которое изменится. Таким образом, вы должны определить стратегию как интерфейс. Обычно вы должны поддерживать интерфейсы над наследованием, если хотите, чтобы объект имел определенный тип; в этом случае MediaStrategy. Опираясь на наследование для идентификации типа опасно; он блокирует вас в определенной иерархии наследования. Java не разрешает множественное наследование, поэтому вы не можете расширять то, что дает вам полезную реализацию или больше идентификатора типа.

12
ответ дан Richard Harrison 25 August 2018 в 04:55
поделиться

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

3
ответ дан Scott Lawrence 25 August 2018 в 04:55
поделиться

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

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

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

2
ответ дан spoulson 25 August 2018 в 04:55
поделиться

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

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

1
ответ дан sunwukung 25 August 2018 в 04:55
поделиться

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

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

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

3
ответ дан theschmitzer 25 August 2018 в 04:55
поделиться

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

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

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

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

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

3
ответ дан Wheat 25 August 2018 в 04:55
поделиться

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

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

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

  1. Несколько несвязанных классов хотят поддерживать протокол.
  2. Эти классы уже установили базовые классы (например, некоторые из них - пользовательский интерфейс ( UI), а некоторые - веб-службы XML).
  3. Агрегирование не подходит или практически невозможно. Во всех других ситуациях наследование классов является лучшей моделью.
6
ответ дан YeahStu 25 August 2018 в 04:55
поделиться

Еще одна возможность иметь в виду использование отношения «has-a», иначе «реализовано в терминах» или «композиции». Иногда это более чистый и более гибкий способ структурирования вещей, чем использование наследования «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); }
}

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

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

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

106
ответ дан 2 revs 25 August 2018 в 04:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: