Источник: http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C # - замечательный язык, который развился и развился за последние 14 лет. Это здорово для нас, разработчиков, потому что зрелый язык предоставляет нам множество языковых функций, которые в нашем распоряжении.
Однако большая власть становится большой ответственностью. Некоторые из этих функций могут использоваться не по назначению, или иногда бывает трудно понять, почему вы решили использовать одну функцию вместо другой. На протяжении многих лет я видел, как многие разработчики сталкиваются с проблемой, когда выбирать, использовать интерфейс или использовать абстрактный класс. У обоих есть свои преимущества и недостатки, а также правильное время и место для каждого из них. Но как решить ???
Оба обеспечивают повторное использование общих функций между типами. Наиболее очевидным отличием сразу является то, что интерфейсы не предоставляют реализации для своей функциональности, тогда как абстрактные классы позволяют вам реализовать какое-то «базовое» или «стандартное» поведение, а затем иметь возможность «переопределить» это поведение по умолчанию с помощью производных типов классов, если это необходимо. .
Это все хорошо, обеспечивает многократное использование кода и соответствует принципу DRY (не повторяйтесь) при разработке программного обеспечения. Абстрактные классы удобно использовать, когда у вас есть отношения «есть».
Например: золотистый ретривер - это «собака». Как и пудель. Они оба умеют лаять, как и все собаки. Однако вы можете заявить, что парк пуделей значительно отличается от «стандартного» собачьего лая. Поэтому для вас может иметь смысл реализовать что-то следующим образом:
public abstract class Dog
{
public virtual void Bark()
{
Console.WriteLine("Base Class implementation of Bark");
}
}
public class GoldenRetriever : Dog
{
// the Bark method is inherited from the Dog class
}
public class Poodle : Dog
{
// here we are overriding the base functionality of Bark with our new implementation
// specific to the Poodle class
public override void Bark()
{
Console.WriteLine("Poodle's implementation of Bark");
}
}
// Add a list of dogs to a collection and call the bark method.
void Main()
{
var poodle = new Poodle();
var goldenRetriever = new GoldenRetriever();
var dogs = new List<Dog>();
dogs.Add(poodle);
dogs.Add(goldenRetriever);
foreach (var dog in dogs)
{
dog.Bark();
}
}
// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark
//
Как видите, это был бы отличный способ сохранить ваш код DRY и позволить вызывать реализацию базового класса, когда любой из типов может просто полагаться на Bark по умолчанию, а не на реализацию специального случая. Такие классы, как GoldenRetriever, Boxer, Lab, могут бесплатно унаследовать Bark «по умолчанию» (класс басов) только потому, что они реализуют абстрактный класс Dog.
Но я уверен, вы это уже знали.
Вы здесь, потому что хотите понять, почему вы можете предпочесть интерфейс абстрактному классу или наоборот. Одна из причин, по которой вы можете захотеть выбрать интерфейс вместо абстрактного класса, - это когда у вас нет или вы хотите предотвратить реализацию по умолчанию. Обычно это происходит потому, что типы, реализующие интерфейс, не связаны отношениями «есть». На самом деле, они вовсе не обязательно должны быть связаны друг с другом, за исключением того факта, что каждый тип «способен» или имеет «способность» что-то делать или иметь.
Что, черт возьми, это значит? Ну, например: человек - не утка… а утка - не человек. Довольно очевидно. Однако и утка, и человек обладают «умением» плавать (при условии, что человек прошел уроки плавания в 1-м классе :)). Кроме того, поскольку утка не является человеком или наоборот, это не отношение «есть», а отношения «способен», и мы можем использовать интерфейс, чтобы проиллюстрировать это:
// Create ISwimable interface
public interface ISwimable
{
public void Swim();
}
// Have Human implement ISwimable Interface
public class Human : ISwimable
public void Swim()
{
//Human's implementation of Swim
Console.WriteLine("I'm a human swimming!");
}
// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
public void Swim()
{
// Duck's implementation of Swim
Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
}
}
//Now they can both be used in places where you just need an object that has the ability "to swim"
public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
somethingThatCanSwim.Swim();
}
public void Main()
{
var human = new Human();
var duck = new Duck();
var listOfThingsThatCanSwim = new List<ISwimable>();
listOfThingsThatCanSwim.Add(duck);
listOfThingsThatCanSwim.Add(human);
foreach (var something in listOfThingsThatCanSwim)
{
ShowHowYouSwim(something);
}
}
// So at runtime the correct implementation of something.Swim() will be called
// Output:
// Quack! Quack! I'm a Duck swimming!
// I'm a human swimming!
Использование интерфейсов, подобных приведенному выше, позволит вам передать объект в метод, который «может» что-то делать. Код не заботится о том, как он это делает ... Все, что он знает, - это то, что он может вызвать метод Swim для этого объекта, и этот объект будет знать, какое поведение будет выполняться во время выполнения, в зависимости от его типа.
Еще раз, это помогает вашему коду оставаться СУХИМ, так что вам не придется писать несколько методов, вызывающих объект для выполнения одной и той же базовой функции (ShowHowHumanSwims (человек), ShowHowDuckSwims (утка) и т. Д.)
Использование интерфейса здесь позволяет вызывающим методам не беспокоиться о том, какой тип и как реализовано поведение. Он просто знает, что с учетом интерфейса каждый объект должен будет реализовать метод Swim, поэтому его можно безопасно вызывать в его собственном коде и разрешать обработку поведения метода Swim в его собственном классе.
Резюме:
Итак, мое основное практическое правило - использовать абстрактный класс, когда вы хотите реализовать функциональность «по умолчанию» для иерархии классов или / и классы или типы, с которыми вы работаете, разделяют отношение «есть» (например, пудель «это Тип собаки).
С другой стороны, используйте интерфейс, когда у вас нет отношений «есть», но есть типы, которые разделяют «способность» что-то делать или иметь что-то (например, Утка «не» человек. «Умение» плавать).
Еще одно различие между абстрактными классами и интерфейсами, которое следует отметить, заключается в том, что класс может реализовывать от одного до многих интерфейсов, но класс может наследовать только от ОДНОГО абстрактного класса (или любого класса в этом отношении). Да, вы можете вкладывать классы и иметь иерархию наследования (которая есть и должна быть во многих программах), но вы не можете наследовать два класса в одном определении производного класса (это правило применяется к C #. В некоторых других языках это возможно, обычно только из-за отсутствия интерфейсов на этих языках).
Также помните, что при использовании интерфейсов следует придерживаться принципа разделения интерфейсов (ISP). Интернет-провайдер заявляет, что ни один клиент не должен зависеть от методов, которые он не использует. По этой причине интерфейсы должны быть ориентированы на конкретные задачи и обычно очень маленькие (например, IDisposable, IComparable).
Еще один совет: если вы разрабатываете небольшие краткие функциональные возможности, используйте интерфейсы. Если вы разрабатываете большие функциональные блоки, используйте абстрактный класс.
Надеюсь, это проясняет ситуацию для некоторых людей!
Также, если вы можете придумать какие-либо лучшие примеры или хотите что-то указать, сделайте это в комментариях ниже!
person
Community
schedule
09.12.2014
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.
. Я не могу понять, что имеется в виду в отрывке. Мы можем создать несколько базовых типов и создать производный тип для любого из них, чтобы разработчик мог выбрать базовый тип. Может кто-нибудь объяснить, пожалуйста, что мне не хватает? Я считаю, что это может быть частью этого вопроса. Или я должен опубликовать еще один о конкретном отрывке? - person qqqqqqq   schedule 06.03.2020