Когда я должен использовать интерфейс и когда я должен использовать базовый класс?
Должен ли он всегда быть интерфейсом, если я не хочу определять базовую реализацию методов?
Если у меня есть класс собак и кошек. Почему я хотел бы реализовать IPet вместо PetBase? Я могу понять наличие интерфейсов для ISheds или IBarks (IMakesNoise?), Потому что они могут быть размещены на основе питомца за питомцем, но я не понимаю, какой использовать для обычного питомца.
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.
. Я не могу понять, что подразумевается в выдержке. Мы можем создать несколько базовых типов и создать производный тип для любого из них, чтобы разработчик мог выбрать базовый тип. Может кто-нибудь объяснить, пожалуйста, что мне не хватает? Я считаю, что это может быть частью этого вопроса. Или я должен опубликовать еще один о конкретной выдержке?Ответы:
Давайте возьмем ваш пример классов Dog и Cat и проиллюстрируем это на C #:
И собака, и кошка - животные, в частности, четвероногие млекопитающие (животные слишком часто встречаются). Давайте предположим, что у вас есть абстрактный класс Mammal для них обоих:
Этот базовый класс, вероятно, будет иметь методы по умолчанию, такие как:
Все это поведение, которое имеет более или менее одинаковую реализацию для обоих видов. Чтобы определить это у вас будет:
Теперь предположим, что есть другие млекопитающие, которых мы обычно будем видеть в зоопарке:
Это все еще будет в силе, потому что в основе функциональности
Feed()
иMate()
останется прежним.Тем не менее, жирафы, носороги и бегемоты не совсем животные, из которых можно сделать домашних животных. Вот где интерфейс будет полезен:
Реализация вышеуказанного контракта не будет одинаковой между кошкой и собакой; помещать их реализации в абстрактный класс для наследования будет плохой идеей.
Ваши определения собак и кошек теперь должны выглядеть так:
Теоретически вы можете переопределить их из более высокого базового класса, но по существу интерфейс позволяет добавлять в класс только то, что вам нужно, без необходимости наследования.
Следовательно, поскольку вы обычно можете наследовать только от одного абстрактного класса (в большинстве статически типизированных ОО-языков, то есть ... исключения включают в себя C ++), но способны реализовывать несколько интерфейсов, это позволяет вам создавать объекты строго по мере необходимости .
источник
Ну, Джош Блох сказал себе в Effective Java 2d :
Предпочитаю интерфейсы абстрактным классам
Некоторые основные моменты:
С другой стороны, интерфейсы очень трудно развиваться. Если вы добавите метод к интерфейсу, он сломает все его реализации.
PS .: Купить книгу. Это намного более подробно.
источник
Интерфейсы и базовые классы представляют две разные формы отношений.
Наследование (базовые классы) представляет собой отношение «есть». Например, собака или кошка - это домашнее животное. Эти отношения всегда представляют (единственную) цель класса (в сочетании с «принципом единой ответственности» ).
Интерфейсы , с другой стороны, представляют дополнительные функции класса. Я бы назвал это «есть», как в «
Foo
одноразовых», следовательно,IDisposable
интерфейс в C #.источник
Современный стиль - это определение IPet и PetBase.
Преимущество интерфейса в том, что другой код может использовать его без каких-либо связей с другим исполняемым кодом. Полностью "чистый". Также интерфейсы могут быть смешаны.
Но базовые классы полезны для простых реализаций и общих утилит. Так что предоставьте также абстрактный базовый класс, чтобы сэкономить время и код.
источник
Интерфейсы
Базовые классы
источник
В общем, вы должны отдавать предпочтение интерфейсам перед абстрактными классами. Одна из причин использовать абстрактный класс - если у вас есть общая реализация среди конкретных классов. Конечно, вы все равно должны объявить интерфейс (IPet) и иметь абстрактный класс (PetBase), реализующий этот интерфейс. Используя небольшие отдельные интерфейсы, вы можете использовать множественные числа для дальнейшего повышения гибкости. Интерфейсы обеспечивают максимальную гибкость и переносимость типов через границы. При передаче ссылок через границы всегда передавайте интерфейс, а не конкретный тип. Это позволяет принимающей стороне определить конкретную реализацию и обеспечивает максимальную гибкость. Это абсолютно верно при программировании в стиле TDD / BDD.
«Брига четырех» заявила в своей книге «Поскольку наследование подвергает подкласс деталям реализации его родителя, часто говорят, что« наследование нарушает инкапсуляцию ». Я верю, что это правда.
источник
Это довольно специфично для .NET, но в книге «Руководство по разработке инфраструктуры» утверждается, что в целом классы предоставляют больше гибкости в развивающейся среде. После того, как интерфейс поставляется, вы не сможете изменить его, не нарушив код, который использовал этот интерфейс. Однако с помощью класса вы можете изменить его и не нарушать код, ссылающийся на него. Пока вы делаете правильные модификации, включая добавление новых функций, вы сможете расширять и развивать свой код.
Кшиштоф Квалина говорит на странице 81:
При этом, безусловно, есть место для интерфейсов. В качестве общего руководства всегда предоставляйте абстрактную реализацию интерфейса базового класса, если не для чего иного, как пример способа реализации интерфейса. В лучшем случае этот базовый класс сэкономит много работы.
источник
Хуан,
Мне нравится думать об интерфейсах как о способе охарактеризовать класс. Конкретный класс породы собак, например, YorkshireTerrier, может быть потомком родительского класса собак, но он также реализует IFurry, IStubby и IYippieDog. Таким образом, класс определяет, что это за класс, но интерфейс рассказывает нам об этом.
Преимущество этого в том, что он позволяет мне, например, собрать все IYippieDog и выбросить их в мою коллекцию Ocean. Так что теперь я могу охватить определенный набор объектов и найти те, которые соответствуют критериям, на которые я смотрю, без слишком тщательного изучения класса.
Я считаю, что интерфейсы действительно должны определять подмножество публичного поведения класса. Если он определяет все общедоступное поведение для всех реализуемых классов, то он обычно не должен существовать. Они не говорят мне ничего полезного.
Эта мысль противоречит идее, что у каждого класса должен быть интерфейс, и вы должны написать код для интерфейса. Это нормально, но вы в конечном итоге получаете множество индивидуальных интерфейсов с классами, и это сбивает с толку. Я понимаю, что идея в том, что на самом деле ничего не стоит делать, и теперь вы можете с легкостью менять местами. Однако я нахожу, что я делаю это редко. Большую часть времени я просто изменяю существующий класс на месте, и у меня возникают те же самые проблемы, которые я всегда делал, если публичный интерфейс этого класса нуждается в изменении, за исключением того, что теперь я должен изменить его в двух местах.
Так что, если вы думаете, как я, вы бы определенно сказали, что Cat и Dog являются IPettable. Это характеристика, которая соответствует им обоим.
Другая часть этого, хотя они должны иметь тот же базовый класс? Вопрос в том, должны ли они рассматриваться как одно и то же. Конечно, они оба Животные, но соответствует ли это тому, как мы собираемся использовать их вместе.
Скажем, я хочу собрать все классы животных и положить их в мой контейнер с ковчегом.
Или они должны быть млекопитающими? Может быть, нам нужен какой-нибудь завод по производству животных?
Они вообще должны быть связаны друг с другом? Достаточно ли просто знать, что они оба IPettable?
Я часто чувствую желание получить целую иерархию классов, когда мне действительно нужен только один класс. Я делаю это в ожидании, когда-нибудь мне это понадобится, и обычно я никогда этого не делаю. Даже когда я это делаю, мне обычно приходится много делать, чтобы это исправить. Это потому, что первый класс, который я создаю, - это не Собака, мне не так повезло, это вместо Утконоса. Теперь вся моя иерархия классов основана на странном случае, и у меня много потерянного кода.
Вы также можете обнаружить, что не все кошки являются IPettable (как этот безволосый). Теперь вы можете переместить этот интерфейс ко всем подходящим производным классам. Вы обнаружите, что гораздо менее разрушительное изменение, что внезапно Cats больше не являются производными от PettableBase.
источник
Вот основное и простое определение интерфейса и базового класса:
ура
источник
Я рекомендую использовать композицию вместо наследования, когда это возможно. Используйте интерфейсы, но используйте объекты-члены для базовой реализации. Таким образом, вы можете определить фабрику, которая будет конструировать ваши объекты так, чтобы они вели себя определенным образом. Если вы хотите изменить поведение, вы создаете новый фабричный метод (или абстрактную фабрику), который создает различные типы подобъектов.
В некоторых случаях вы можете обнаружить, что вашим основным объектам вообще не нужны интерфейсы, если все изменяемое поведение определено в вспомогательных объектах.
Таким образом, вместо IPet или PetBase вы можете получить Pet с параметром IFurBehavior. Параметр IFurBehavior устанавливается методом CreateDog () PetFactory. Именно этот параметр вызывается для метода shed ().
Если вы сделаете это, вы обнаружите, что ваш код гораздо более гибок, и большинство ваших простых объектов имеют дело с очень базовым поведением всей системы.
Я рекомендую этот шаблон даже в языках с множественным наследованием.
источник
Это хорошо объяснено в этой статье Java World .
Лично я склонен использовать интерфейсы для определения интерфейсов - то есть частей системного дизайна, которые определяют, как к чему-то получить доступ.
Нередко у меня будет класс, реализующий один или несколько интерфейсов.
Абстрактные классы я использую как основу для чего-то другого.
Ниже приводится выдержка из вышеупомянутой статьи JavaWorld.com, автор Тони Синтес, 20.04.01
Некоторые говорят, что вы должны определить все классы в терминах интерфейсов, но я думаю, что рекомендации кажутся немного экстремальными. Я использую интерфейсы, когда вижу, что что-то в моем дизайне будет часто меняться.
источник
Также имейте в виду, что в OO не смейтесь ( см. Блог ) и всегда моделируйте объекты, основываясь на требуемом поведении, если вы разрабатываете приложение, в котором вам требуется только общее имя и вид для животного, тогда вам нужно только один класс Animal со свойством для имени, вместо миллионов классов для каждого возможного животного в мире.
источник
У меня есть грубое эмпирическое правило
Функциональность: вероятно, будет отличаться во всех частях: Интерфейс.
Данные и функциональность, части будут в основном одинаковыми, части разные: абстрактный класс.
Данные и функциональность, реально работающие, если расширены только с небольшими изменениями: обычный (конкретный) класс
Данные и функциональность, никаких изменений не планируется: обычный (конкретный) класс с финальным модификатором.
Данные и, возможно, функциональность: только для чтения: члены enum.
Это очень грубо и готово и совсем не определено строго, но есть спектр интерфейсов, где все должно быть изменено на перечисления, где все исправлено немного как файл только для чтения.
источник
Интерфейсы должны быть маленькими. Действительно маленький. Если вы действительно разбиваете свои объекты, то ваши интерфейсы, вероятно, будут содержать только несколько очень специфических методов и свойств.
Абстрактные классы - это ярлыки. Существуют ли вещи, которые разделяют все производные PetBase, которые вы можете кодировать один раз и покончить с ними? Если да, то пришло время для абстрактного класса.
Абстрактные классы также ограничены. В то время как они дают вам быстрый способ создания дочерних объектов, любой данный объект может реализовать только один абстрактный класс. Много раз я нахожу это ограничением абстрактных классов, и именно поэтому я использую много интерфейсов.
Абстрактные классы могут содержать несколько интерфейсов. Ваш абстрактный класс PetBase может реализовывать IPet (домашние животные имеют владельцев) и IDigestion (домашние животные едят или, по крайней мере, должны). Тем не менее, PetBase, вероятно, не будет реализовывать IMammal, поскольку не все домашние животные являются млекопитающими и не все млекопитающие являются домашними животными. Вы можете добавить MammalPetBase, который расширяет PetBase, и добавить IMammal. FishBase может иметь PetBase и добавить IFish. IFish будет иметь ISwim и IUnderwaterBreather в качестве интерфейсов.
Да, мой пример чрезмерно сложен для простого примера, но это является отличной особенностью совместной работы интерфейсов и абстрактных классов.
источник
Источник : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C # - замечательный язык, который вырос и развился за последние 14 лет. Это отлично подходит для нас, разработчиков, потому что зрелый язык предоставляет нам множество языковых возможностей, которые находятся в нашем распоряжении.
Однако с большой властью становится большая ответственность. Некоторые из этих функций могут быть использованы не по назначению, или иногда трудно понять, почему вы решили использовать одну функцию над другой. На протяжении многих лет мне приходилось сталкиваться с проблемой, с которой сталкивались многие разработчики, - когда выбирать, использовать ли интерфейс или использовать абстрактный класс. Оба имеют свои преимущества и недостатки, а также правильное время и место для использования каждого. Но как нам решить ???
Оба предусматривают повторное использование общих функций между типами. Наиболее очевидное различие заключается в том, что интерфейсы не обеспечивают реализацию своей функциональности, тогда как абстрактные классы позволяют вам реализовать некоторое «базовое» или «стандартное» поведение, а затем имеют возможность «переопределить» это поведение по умолчанию с производными типами классов, если это необходимо. ,
Это все хорошо и хорошо, и обеспечивает отличное повторное использование кода и придерживается принципа DRY (не повторяй себя) разработки программного обеспечения. Абстрактные классы прекрасно использовать, когда у вас есть отношения «есть».
Например: золотистый ретривер - это тип собаки. Так же как и пудель. Они оба могут лаять, как все собаки. Тем не менее, вы можете указать, что парк пуделей значительно отличается от лая для собак по умолчанию. Поэтому для вас может иметь смысл реализовать следующее:
Как вы можете видеть, это было бы отличным способом сохранить ваш код СУХИМЫМ и позволить вызывать реализацию базового класса, когда любой из типов может просто полагаться на стандартную Bark вместо специальной реализации. Такие классы, как GoldenRetriever, Boxer, Lab, все могут унаследовать Bark «по умолчанию» (класс баса) просто потому, что они реализуют абстрактный класс Dog.
Но я уверен, что вы уже знали это.
Вы здесь, потому что хотите понять, почему вы можете выбрать интерфейс вместо абстрактного класса или наоборот. Ну, одна из причин, по которой вы можете выбрать интерфейс вместо абстрактного класса, это то, что у вас нет или вы хотите предотвратить реализацию по умолчанию. Обычно это происходит потому, что типы, которые реализуют интерфейс, не связаны в отношении «является». На самом деле, они вообще не должны быть связаны, за исключением того факта, что каждый тип «способен» или имеет «способность» что-то делать или что-то иметь.
Что, черт возьми, это значит? Ну, например: человек не утка ... и утка не человек. Довольно очевидно. Однако и утка, и человек обладают способностью плавать (учитывая, что человек прошел свои уроки плавания в 1-м классе :)). Кроме того, поскольку утка - это не человек или наоборот, это не «реализация», а отношения «способный», и мы можем использовать интерфейс, чтобы проиллюстрировать это:
Использование интерфейсов, подобных приведенному выше, позволит вам передать объект в метод, который «способен» что-то делать. Код не заботится о том, как он это делает ... Все, что он знает, это то, что он может вызывать метод Swim для этого объекта, и этот объект будет знать, какое поведение будет выполняться во время выполнения, в зависимости от его типа.
Опять же, это помогает вашему коду оставаться сухим, так что вам не придется писать несколько методов, вызывающих объект, для преобразования одной и той же основной функции (ShowHowHumanSwims (human), ShowHowDuckSwims (duck) и т. Д.)
Использование здесь интерфейса позволяет вызывающим методам не беспокоиться о том, какой тип или как реализовано поведение. Он просто знает, что с учетом интерфейса каждый объект должен был реализовать метод Swim, поэтому его можно безопасно вызывать в своем собственном коде и разрешать поведение метода Swim в своем собственном классе.
Резюме:
Поэтому мое основное правило - использовать абстрактный класс, если вы хотите реализовать функциональность «по умолчанию» для иерархии классов и / или классы или типы, с которыми вы работаете, используют отношение «есть» (например, пудель «это Тип собаки).
С другой стороны, используйте интерфейс, когда у вас нет отношений «есть», но есть типы, которые разделяют «способность» что-то делать или что-то иметь (например, утка «не» является человеком. Однако утка и человеческая доля «Умение» плавать).
Еще одно различие, которое следует отметить между абстрактными классами и интерфейсами, заключается в том, что класс может реализовывать один-много интерфейсов, но класс может наследовать только от ОДНОГО абстрактного класса (или любого класса в этом отношении). Да, вы можете вкладывать классы и иметь иерархию наследования (что многие программы делают и должны иметь), но вы не можете наследовать два класса в одном определении производного класса (это правило применяется к C #. В некоторых других языках вы можете сделать это, обычно только из-за отсутствия интерфейсов на этих языках).
Также помните, что при использовании интерфейсов следует придерживаться принципа разделения интерфейса (ISP). Провайдер заявляет, что ни один клиент не должен зависеть от методов, которые он не использует. По этой причине интерфейсы должны быть ориентированы на конкретные задачи и обычно очень малы (например, IDisposable, IComparable).
Другой совет: если вы разрабатываете небольшие, краткие кусочки функциональности, используйте интерфейсы. Если вы разрабатываете большие функциональные блоки, используйте абстрактный класс.
Надеюсь, это прояснит ситуацию для некоторых людей!
Также, если вы можете придумать какие-нибудь лучшие примеры или хотите что-то указать, пожалуйста, сделайте это в комментариях ниже!
источник
Случай с базовыми классами через интерфейсы был хорошо объяснен в Руководстве по кодированию Submain .NET:
источник
Одно важное отличие состоит в том, что вы можете наследовать только один базовый класс, но вы можете реализовать много интерфейсов. Таким образом, вы хотите использовать базовый класс, только если вы абсолютно уверены, что вам не нужно также наследовать другой базовый класс. Кроме того, если вы обнаружите, что ваш интерфейс становится большим, вы должны начать разбивать его на несколько логических частей, определяющих независимую функциональность, поскольку нет правила, что ваш класс не может реализовать их все (или что вы можете определить другой интерфейс, который просто наследует их все для группировки).
источник
Когда я впервые начал изучать объектно-ориентированное программирование, я совершил легкую и, вероятно, распространенную ошибку, используя наследование для обмена общим поведением - даже там, где это поведение не было существенным для природы объекта.
Чтобы продолжить опираться на пример, который часто используется в этом конкретном вопросе, есть много вещей, которые можно назвать любимыми - подруги, машины, нечеткие одеяла ... - так что я мог бы иметь класс Petable, обеспечивающий это общее поведение, и наследовать различные классы от него.
Тем не менее, быть пригодным для питья не является частью природы любого из этих объектов. Существуют гораздо более важные понятия, которые важны для их характера: девушка - это человек, автомобиль - это наземное транспортное средство, кот - это млекопитающее ...
Поведения должны быть сначала назначены интерфейсам (включая интерфейс класса по умолчанию) и повышены до базового класса, только если они (а) являются общими для большой группы классов, которые являются подмножествами более крупного класса - в том же смысле, что «кошка» и «человек» являются подмножествами «млекопитающих».
Суть в том, что после того, как вы поймете, что объектно-ориентированное проектирование достаточно хорошо, чем я сначала, вы обычно будете делать это автоматически, даже не задумываясь об этом. Таким образом, чистая правда утверждения «код для интерфейса, а не абстрактного класса» становится настолько очевидной, что вам трудно поверить, что кто-то потрудится сказать это - и начать пытаться воспринимать в нем другие значения.
Еще я хотел бы добавить, что если класс является чисто абстрактным - без неабстрактных, не наследуемых членов или методов, предоставляемых потомку, родителю или клиенту, - тогда почему он является классом? Его можно заменить, в некоторых случаях, интерфейсом, а в других случаях - нулем.
источник
Предпочитаю интерфейсы абстрактным классам
Обоснование, основные моменты для рассмотрения [два уже упомянутых здесь] являются:
Конечно, предмет был подробно обсужден в другом месте [2,3].
[1] Конечно, он добавляет больше кода, но если краткость является вашей главной задачей, вам, вероятно, следовало бы избегать Java в первую очередь!
[2] Джошуа Блох, Эффективная Ява, пункты 16-18.
[3] http://www.codeproject.com/KB/ar ...
источник
Предыдущие комментарии об использовании абстрактных классов для общей реализации, безусловно, на высоте. Одно преимущество, о котором я еще не упомянул, заключается в том, что использование интерфейсов значительно упрощает реализацию фиктивных объектов с целью модульного тестирования. Определение IPet и PetBase, как описано Джейсоном Коэном, позволяет легко смоделировать различные условия данных без затрат на физическую базу данных (пока вы не решите, что пришло время протестировать реальную вещь).
источник
Не используйте базовый класс, если вы не знаете, что это значит, и что он применяется в этом случае. Если это применимо, используйте его, иначе используйте интерфейсы. Но обратите внимание на ответ о небольших интерфейсах.
Публичное наследование чрезмерно используется в OOD и выражает гораздо больше, чем большинство разработчиков осознают или хотят соответствовать. См. Принцип замещения Лискова
Короче говоря, если A «представляет собой» B, то A требует не более B и обеспечивает не менее B для каждого раскрываемого им метода.
источник
Другой вариант, о котором следует помнить, - это использование отношения «имеет», иначе «реализуется в терминах» или «состав». Иногда это более чистый, более гибкий способ структурирования вещей, чем использование наследования "is-a".
Возможно, логически не имеет смысла говорить, что у Собаки и Кошки «есть» домашнее животное, но это позволяет избежать распространенных ошибок множественного наследования:
Да, этот пример показывает, что при таком подходе много дублирования кода и недостатка элегантности. Но следует также понимать, что это помогает отделить собаку и кошку от класса домашних животных (в этом случае у собаки и кошки нет доступа к частным членам домашних животных), и это оставляет место для собаки и кошки, чтобы наследовать от чего-то другого. - Возможно, класс Млекопитающих.
Композиция предпочтительна, когда не требуется частный доступ, и вам не нужно ссылаться на собак и кошек, используя общие ссылки / указатели на домашних животных. Интерфейсы дают вам такую общую справочную возможность и могут помочь сократить многословность вашего кода, но они также могут запутывать вещи, когда они плохо организованы. Наследование полезно, когда вам нужен личный доступ, и при его использовании вы берете на себя обязательство связывать свои классы собак и кошек с вашими домашними животными, что требует больших затрат.
Между наследованием, композицией и интерфейсами не существует единственного правильного способа, и это помогает понять, как все три варианта можно использовать в гармонии. Из трех типов наследование обычно является вариантом, который следует использовать реже всего.
источник
Концептуально интерфейс используется для формального и полуформального определения набора методов, которые будет предоставлять объект. Формально означает набор имен и сигнатур методов, а полуформально означает читаемую человеком документацию, связанную с этими методами.
Интерфейсы - это только описания API (в конце концов, API означает интерфейс прикладного программирования ), они не могут содержать какую-либо реализацию, и невозможно использовать или запускать интерфейс. Они только делают явный договор о том, как вы должны взаимодействовать с объектом.
Классы предоставляют реализацию, и они могут объявить, что они реализуют ноль, один или несколько интерфейсов. Если класс предназначен для наследования, соглашение заключается в добавлении префикса имени класса к «Base».
Существует различие между базовым классом и абстрактным базовым классом (ABC). ABC смешивают интерфейс и реализацию вместе. Абстракция вне компьютерного программирования означает «резюме», то есть «абстрактный == интерфейс». Затем абстрактный базовый класс может описывать как интерфейс, так и пустую, частичную или полную реализацию, которая предназначена для наследования.
Мнения о том, когда использовать интерфейсы или абстрактные базовые классы, а не просто классы , будут сильно различаться в зависимости от того, что вы разрабатываете, и от того, на каком языке вы разрабатываете. Интерфейсы часто ассоциируются только со статически типизированными языками, такими как Java или C #, но Динамически типизированные языки также могут иметь интерфейсы и абстрактные базовые классы . В Python, например, различие становится ясным между классом, который заявляет , что она реализует в интерфейс , и объект, который является экземпляром класса , и , как говорят обеспечить , что интерфейс, На динамическом языке возможно, что два объекта, которые являются экземплярами одного и того же класса , могут объявить, что они предоставляют совершенно разные интерфейсы. В Python это возможно только для атрибутов объекта, в то время как методы являются общим состоянием для всех объектов класса . Однако в Ruby объекты могут иметь методы для каждого экземпляра, поэтому возможно, что интерфейс между двумя объектами одного и того же класса может варьироваться настолько, насколько этого желает программист (однако, Ruby не имеет какого-либо явного способа объявления интерфейсов).
В динамических языках интерфейс к объекту часто неявно предполагается, либо путем самоанализа объекта и спрашивая его, какие методы он предоставляет ( посмотрите, прежде чем прыгнуть ), либо, предпочтительно, просто пытаясь использовать желаемый интерфейс на объекте и перехватывая исключения, если объект не предоставляет такой интерфейс ( проще просить прощения, чем разрешения ). Это может привести к «ложным срабатываниям», когда два интерфейса имеют одинаковое имя метода, но семантически различаются. Однако компромисс заключается в том, что ваш код более гибок, поскольку вам не нужно заранее указывать слишком много, чтобы предвидеть все возможные варианты использования вашего кода.
источник
Это зависит от ваших требований. Если IPet достаточно прост, я бы предпочел реализовать это. В противном случае, если PetBase реализует массу функциональных возможностей, которые вы не хотите дублировать, тогда имейте это в виду.
Недостатком реализации базового класса является требование к
override
(илиnew
) существующим методам. Это делает их виртуальными методами, что означает, что вы должны быть осторожны с тем, как вы используете экземпляр объекта.Наконец, единственное наследование .NET убивает меня. Наивный пример: скажем, вы делаете пользовательский элемент управления, поэтому вы наследуете
UserControl
. Но теперь вы заблокированы от наследованияPetBase
. Это вынуждает вас реорганизоваться, например, чтобы сделатьPetBase
члена класса вместо этого.источник
Я обычно не реализую, пока мне не нужно. Я предпочитаю интерфейсы абстрактным классам, потому что это дает немного больше гибкости. Если в некоторых классах наследования есть общее поведение, я поднимаю его и делаю абстрактный базовый класс. Я не вижу необходимости в том и другом, поскольку они, по сути, выполняют одну и ту же задачу, а наличие обоих - неприятный запах кода (imho), что решение было чрезмерно разработанным.
источник
Что касается C #, то в некотором смысле интерфейсы и абстрактные классы могут быть взаимозаменяемыми. Однако различия заключаются в следующем: i) интерфейсы не могут реализовывать код; ii) из-за этого интерфейсы не могут далее вызывать стек до подкласса; и iii) только абстрактный класс может быть унаследован от класса, тогда как в классе может быть реализовано несколько интерфейсов.
источник
По умолчанию интерфейс обеспечивает уровень для связи с другим кодом. Все открытые свойства и методы класса по умолчанию реализуют неявный интерфейс. Мы также можем определить интерфейс как роль, когда любой класс должен играть эту роль, он должен реализовать его, предоставляя ему различные формы реализации в зависимости от класса, реализующего его. Следовательно, когда вы говорите об интерфейсе, вы говорите о полиморфизме, а когда вы говорите о базовом классе, вы говорите о наследовании. Два понятия упс !!!
источник
Я обнаружил, что шаблон Interface> Abstract> Concrete работает в следующем сценарии использования:
Абстрактный класс определяет общие атрибуты по умолчанию для конкретных классов, но обеспечивает интерфейс. Например:
Теперь, поскольку у всех млекопитающих есть волосы и соски (AFAIK, я не зоолог), мы можем свернуть это в абстрактный базовый класс
И тогда конкретные классы просто определяют, что они идут прямо.
Этот дизайн хорош, когда есть много конкретных классов, и вы не хотите поддерживать шаблон просто для программирования интерфейса. Если бы новые методы были добавлены к интерфейсу, это сломало бы все получающиеся классы, таким образом, вы все еще получаете преимущества интерфейсного подхода.
В этом случае реферат также может быть конкретным; однако, абстрактное обозначение помогает подчеркнуть, что этот образец используется.
источник
Наследник базового класса должен иметь отношение «есть». Интерфейс представляет отношение «реализует». Поэтому используйте базовый класс только тогда, когда ваши наследники будут поддерживать отношения.
источник
Используйте интерфейсы для реализации контракта через семейства несвязанных классов. Например, у вас могут быть общие методы доступа для классов, которые представляют коллекции, но содержат радикально разные данные, т.е. один класс может представлять набор результатов из запроса, а другой может представлять изображения в галерее. Кроме того, вы можете реализовать несколько интерфейсов, что позволит вам смешивать (и обозначать) возможности класса.
Используйте Наследование, когда классы имеют общие отношения и, следовательно, имеют сходную структурную и поведенческую характеристику, т. Е. Автомобиль, мотоцикл, грузовик и внедорожник - все типы дорожных транспортных средств, которые могут содержать несколько колес, максимальную скорость
источник