Интерфейс vs абстрактный класс
Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy. Урок 26 из 70.
И интерфейсы, и абстрактные классы позволяют Define контракты, которым должны следовать другие классы, но они служат разным целям и обладают различными возможностями.
interface определяет, что должен делать class, не предоставляя никакой реализации. Это чистый контракт:
public interface IFlyable
{
void Fly();
}
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("Flapping wings");
}
}Класс abstract class может предоставлять как контракт, так и общую реализацию. Это частичный шаблон:
public abstract class Vehicle
{
public string Brand { get; set; }
public void StartEngine()
{
Console.WriteLine("Engine started");
}
public abstract void Drive(); // Должен быть реализован
}
public class Car : Vehicle
{
public override void Drive()
{
Console.WriteLine("Driving on roads");
}
}Ключевые различия сводятся к правилам наследования и назначению.
Класс может реализовать несколько интерфейсов, но унаследоваться только от одного абстрактного класса.
Используй интерфейс, когда несвязанные классы должны иметь общее поведение (например, IFlyable для птиц и самолётов). Используй абстрактный класс, когда связанные классы имеют общий код и состояние (например, транспортные средства используют общий механизм запуска двигателя).
abstract class могут иметь constructor, поля и модификаторы доступа у членов. Традиционно интерфейсы этого не могли, хотя современный C# допускает реализации по умолчанию.
Если сомневаешься, предпочитай интерфейсы ради гибкости — ты всегда сможешь добавить abstract class позже, если станет необходима общая реализация.
Задание
ЛегкоДавайте создадим систему медиаплеера, которая демонстрирует, когда следует использовать интерфейсы, а когда — абстрактные классы. Вы разработаете структуру, в которой несвязанные устройства используют общее поведение воспроизведения через интерфейс, а связанные типы мультимедиа используют общую реализацию через абстрактный класс.
Вы распределите код по четырём файлам:
IPlayable.cs: объявите интерфейс с именемIPlayableв пространстве имёнMedia. Этот интерфейс представляет контракт для всего, что может воспроизводить мультимедиа, — будь то телефон, телевизор или специализированный музыкальный проигрыватель. Он должен объявлять единственный методPlay(), возвращающий строку.MediaFile.cs: объявите абстрактный класс с именемMediaFileв пространстве имёнMedia. Он представляет общую основу для всех мультимедийных файлов. В нём должны быть свойствоTitle(string) и свойствоDuration(int, обозначающее количество секунд). Добавьте конструктор, который задаёт оба значения. Добавьте конкретный метод с именемGetInfo(), возвращающий"{Title} ({Duration}s)". Также объявите абстрактный методGetType(), возвращающий строку, — каждый тип мультимедиа будет определять, к какому виду файла он относится.AudioFile.cs: объявите классAudioFileв пространстве имёнMedia, который наследуется отMediaFileи реализуетIPlayable. Добавьте свойствоArtist(string). Конструктор должен принимать название, длительность и исполнителя. ПереопределитеGetType(), чтобы он возвращал"Audio". РеализуйтеPlay(), чтобы он возвращал"Playing audio: {Title} by {Artist}".Program.cs: в основном файле создайтеAudioFile, используя входные значения. Продемонстрируйте оба аспекта класса: вызовитеGetInfo()иGetType()(унаследованные от абстрактного класса), а затем вызовитеPlay()(из интерфейса). Сохраните аудиофайл в переменной типаIPlayableи снова вызовитеPlay(), чтобы показать полиморфизм интерфейса.
На вход будут поданы три значения:
- Название песни
- Длительность в секундах
- Имя исполнителя
Выведите результат в следующем формате:
Info: {GetInfo() result}
Type: {GetType() result}
Direct: {Play() result}
Via Interface: {Play() result from IPlayable variable}Например, если на вход поданы Bohemian Rhapsody, 354 и Queen, результат должен быть следующим:
Info: Bohemian Rhapsody (354s)
Type: Audio
Direct: Playing audio: Bohemian Rhapsody by Queen
Via Interface: Playing audio: Bohemian Rhapsody by QueenОбратите внимание, как AudioFile использует преимущества обоих подходов: он наследует общую реализацию (GetInfo()) от абстрактного класса и одновременно выполняет контракт IPlayable. Абстрактный класс обеспечивает повторное использование кода для связанных типов мультимедиа, а интерфейс позволяет любому несвязанному классу быть "playable" без общей иерархии наследования.
Попробуйте сами
using System;
using Media;
class Program
{
public static void Main(string[] args)
{
// Прочитать входные значения
string title = Console.ReadLine();
int duration = Convert.ToInt32(Console.ReadLine());
string artist = Console.ReadLine();
// TODO: Создать AudioFile, используя входные значения
// TODO: Вывести Info: за которым следует результат GetInfo()
// TODO: Вывести Type: за которым следует результат GetType()
// TODO: Вывести Direct: за которым следует результат Play()
// TODO: Сохранить аудиофайл в переменную IPlayable
// TODO: Вывести Via Interface: за которым следует результат Play() из переменной IPlayable
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методовПотренируйтесь самостоятельно: Онлайн-компилятор C#