Итак, сегодня у нас очень интересная тема, которая многих беспокоит, и в основном во время интервью.

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

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

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

Оба основаны на идее полиморфизма (один из трех столпов ООП, а два других - наследование и инкапсуляция). В языках программирования и теории типов полиморфизм (от греческого πολύς, polys, много, много и μορφή, morphē, форма, форма) - это использование единого интерфейса для объектов разных типов. ». Я знаю, что это не самое четкое определение, но оставайтесь со мной, мы немного проясним, что это означает.

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

Это все хорошо, обеспечивает возможность многократного использования кода и придерживается принципа DRY (не повторяйтесь) при разработке программного обеспечения. Абстрактные классы удобно использовать, когда у вас есть отношения «есть». Что такое отношения? Что ж, я рада, что вы спросили ...

Золотистый ретривер - это «разновидность» собак. Как и пудель. Они оба умеют лаять, как и все собаки. Однако вы можете указать, что лай пуделя значительно отличается от «стандартного» лая собаки. Следовательно, для вас может иметь смысл реализовать что-то следующим образом:

Как видите, это был бы отличный способ сохранить ваш код DRY и позволить вызывать реализацию базового класса, когда любой из типов может просто полагаться на Bark по умолчанию, а не на реализацию особого случая. Такие классы, как GoldenRetriever, Boxer, Lab, могут бесплатно унаследовать Bark по умолчанию (класс басов) только потому, что они реализуют абстрактный класс Dog. Но я уверен, что вы это уже знали.

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

Итак, что, черт возьми, это значит? Ну, например: человек - не утка… а утка - не человек. Довольно очевидно. Однако и утка, и человек обладают «умением» плавать (при условии, что человек прошел уроки плавания в 1-м классе :)). Кроме того, поскольку утка не является человеком или наоборот, это не отношения «есть», а отношения «способен», и мы можем использовать интерфейс, чтобы проиллюстрировать это.

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

Еще раз, это помогает вашему коду оставаться СУХИМ, так что вам не придется писать несколько методов, вызывающих объект для выполнения одной и той же базовой функции (ShowHowHumanSwims (человек), ShowHowDuckSwims (утка) и т. Д.)

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

Резюме:

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

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

Еще одно различие между абстрактными классами и интерфейсами, которое следует отметить, заключается в том, что класс может реализовывать от одного до многих интерфейсов, но класс может наследовать только от ОДНОГО абстрактного класса (или любого класса в этом отношении). Да, вы можете вкладывать классы и иметь иерархию наследования (которую делают и должны иметь многие программы), но вы не можете наследовать два класса в одном определении производного класса (это правило применяется к C #. В некоторых других языках это возможно, обычно только из-за отсутствия интерфейсов на этих языках).

Также помните, что при использовании интерфейсов следует придерживаться принципа разделения интерфейсов (ISP). Интернет-провайдер заявляет, что ни один клиент не должен зависеть от методов, которые он не использует. По этой причине интерфейсы должны быть ориентированы на конкретные задачи и обычно очень маленькие (например, IDisposable, IComparable).

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

Надеюсь, это проясняет ситуацию для некоторых людей!

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

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