JavaRush /Курси /C# SELF /Поняття інтерфейсу та його синтаксис

Поняття інтерфейсу та його синтаксис

C# SELF
Рівень 23 , Лекція 0
Відкрита

1. Що таке інтерфейс?

Якщо абстрактний клас — це умовний «напівфабрикат» із частковою реалізацією, то інтерфейс — це просто список вимог: що має уміти та чи інша істота (клас), щоб її можна було використовувати в певному абстрактному контексті.

У програмуванні інтерфейс — це «контракт» або «список вимог» до поведінки об’єкта. Він описує набір публічних методів, властивостей, індексаторів і подій, які клас, що реалізує цей інтерфейс, зобов’язаний надати. Інтерфейс каже: «Якщо ви хочете називатися, наприклад, IDriveable (здатним їздити), то, будь ласка, надайте методи Drive() і Stop()».

Можна уявити інтерфейс як список вимог до працівника: наприклад, якщо ви хочете взяти на роботу «кухарів», то в інтерфейсі буде написано: «Має вміти готувати, подавати страви, мити руки». Як саме кухар це робитиме — його особиста справа. Головне — щоб зовні він працював за контрактом.

Важливо: інтерфейс описує лише що клас має надати, а не як це робиться.

Коротко в термінах ООП

  • Інтерфейс описує зовнішній «вигляд» (API) об’єкта: які дії він дозволяє виконувати.
  • Не містить стану (жодних полів). У класичному розумінні інтерфейси теж не містять реалізації, але в сучасних версіях C# з’явилися винятки (наприклад, методи за замовчуванням), про які ми поговоримо пізніше.
  • Один клас може реалізовувати скільки завгодно інтерфейсів (на відміну від наслідування класів!).

Аналогія з життя

USB-порт — кожен знає, що можна під’єднати мишу, клавіатуру, флешку, навушники, навіть кавоварку (так, трапляється й таке!). Що всередині «миші» — неважливо, аби був роз’єм USB і пристрій підтримував належний протокол. Ось це — інтерфейс!

Навіщо потрібні інтерфейси?

  • Послаблюють зв’язність. Код працює з інтерфейсом, а не з конкретним класом. Програмуємо «на рівні інтерфейсу».
  • Дозволяють реалізувати множинне наслідування поведінки: клас може реалізовувати кілька інтерфейсів.
  • Стандартизація: можна створювати універсальні механізми: наприклад, усі об’єкти, які можна порівнювати, реалізують інтерфейс IComparable.
  • Тестованість: легко підмінити конкретну реалізацію «заглушкою» на тестах.
  • Підтримка плагінів: можна додавати нові «модулі» без змін наявного коду.

2. Синтаксис оголошення інтерфейсу

Переходимо до коду! У C# інтерфейс оголошується за допомогою ключового слова interface, а імена інтерфейсів прийнято починати з I:


// Оголошення інтерфейсу
public interface IPrintable
{
    // Абстрактний метод — контракт
    void Print();

    // Можна також оголошувати властивості
    string Name { get; set; }
}

Запам’ятайте: методи і властивості інтерфейсу НЕ містять реалізації (немає тіла методу, лише сигнатура — так само, як і в абстрактних методах).

Ключові особливості інтерфейсу

  • Не можна оголошувати поля (змінні-члени) всередині інтерфейсу.
  • Усі методи, властивості, події та індексатори за замовчуванням — public (і такими мають бути в реалізації).
  • Інтерфейс не може містити конструкторів (бо він не має стану).
  • Інтерфейс не залежить від того, чи є клас, що його реалізує, абстрактним чи конкретним.

Інтерфейс у дії

Додаймо інтерфейс до нашого навчального застосунку. Нехай у нас є інтерфейс «друковане» (IPrintable), який реалізує клас Report і, наприклад, новий клас Invoice.

public interface IPrintable
{
    void Print();
    string Name { get; set; }
}

Тепер визначимо клас, що реалізує цей інтерфейс:

public class Report : IPrintable
{
    public string Name { get; set; }

    public Report(string name)
    {
        Name = name;
    }

    // Реалізація методу з інтерфейсу
    public void Print()
    {
        Console.WriteLine($"Друк звіту: {Name}");
    }
}

А тепер — інший клас, але з тим самим інтерфейсом:

public class Invoice : IPrintable
{
    public string Name { get; set; }

    public Invoice(string name)
    {
        Name = name;
    }

    public void Print()
    {
        Console.WriteLine($"Друк рахунку: {Name}");
    }
}

Тепер ми можемо написати метод, який працює з будь-якою «друкованою» сутністю:

public static void PrintAnything(IPrintable printable)
{
    printable.Print(); // Усе! Неважливо, звіт це чи рахунок — головне, що вміє друкувати.
}

А ось і приклад використання:

var report = new Report("Щомісячний звіт");
var invoice = new Invoice("Рахунок № 12345");

PrintAnything(report);  // Друк звіту: Щомісячний звіт
PrintAnything(invoice); // Друк рахунку: Рахунок № 12345

Ось так інтерфейси дозволяють писати універсальний, розширюваний і виразний код.

3. Реалізація інтерфейсів

Реалізація інтерфейсу в класі

Інтерфейс реалізується класом за допомогою двокрапки (так-так, як під час наслідування):

public class Ticket : IPrintable
{
    public string Name { get; set; }
    public void Print()
    {
        Console.WriteLine($"Друк квитка: {Name}");
    }
}

Важливо: клас зобов’язаний реалізувати всі члени інтерфейсу. При цьому реалізовані члени мають бути public.

Якщо не реалізувати хоча б один член:

public class BrokenTicket : IPrintable
{
    // Пропущена реалізація Print()
    public string Name { get; set; }
}
// Помилка компіляції: 'BrokenTicket' не реалізує член інтерфейсу 'IPrintable.Print()'

Кілька інтерфейсів

Клас може реалізовувати кілька інтерфейсів через кому:

public interface IStorable
{
    void Store();
}

public class MultiPurposeDoc : IPrintable, IStorable
{
    public string Name { get; set; }
    public void Print()
    {
        Console.WriteLine("Друк документа");
    }

    public void Store()
    {
        Console.WriteLine("Збереження документа");
    }
}

4. Навіщо потрібні інтерфейси?

Можливо, зараз у вас крутиться думка: «Ну добре, синтаксис зрозумілий. А навіщо це все потрібно в реальному житті, окрім як щоб ускладнити мені життя на цьому курсі?» Мушу вас розчарувати: інтерфейси — один із найчастіше використовуваних інструментів у професійній розробці.

Розділення відповідальності та слабка зв’язність (Decoupling / Loose Coupling):

  • Уявіть, що ви розробляєте плеєр для відтворення музики. Йому неважливо, звідки береться музика — з локального файлу, з інтернету чи з CD-диска. Важливо лише, щоб джерело музики могло надати аудіопотік.
  • Ви можете визначити інтерфейс IAudioSource з методом GetAudioStream().
  • Тоді у вас будуть класи FileAudioSource, InternetAudioSource, CDAudioSource, які реалізують цей інтерфейс.
  • Ваш плеєр працюватиме з IAudioSource, не знаючи конкретного типу. Якщо завтра з’явиться новий тип джерела, наприклад, BluetoothAudioSource, вам не доведеться змінювати код плеєра! Просто створіть новий клас, що реалізує IAudioSource. Це робить вашу систему набагато гнучкішою і легко розширюваною. Це слабка зв’язність — компоненти залежать від абстракцій (інтерфейсів), а не від конкретних реалізацій.

Поліморфізм та уніфікована обробка:

Як ми бачили у прикладі з PrintAnything, ви можете мати набір об’єктів різних типів, об’єднаних спільною поведінкою, описаною в інтерфейсі. Ви викликаєте один і той самий метод (Print()) у всіх таких об’єктів, не знаючи, хто саме перед вами — звіт, рахунок чи квиток. Це дозволяє писати дуже лаконічний і універсальний код.

Модульне тестування (unit testing):

Це, мабуть, одне з найважливіших застосувань інтерфейсів. Коли ви тестуєте певний компонент своєї системи, йому часто потрібні інші компоненти для роботи (наприклад, клас, що зберігає дані, може залежати від класу, який працює з базою даних).

Замість того щоб передавати справжній клас DatabaseSaver (який вимагає реальної бази даних для тестування!), ви можете передати підмінний (mock) об’єкт, який просто реалізує інтерфейс IDataSaver. Такий mock-об’єкт лише імітуватиме збереження, не звертаючись до реальної бази. Це дозволяє тестувати компоненти ізольовано, швидко й без зовнішніх залежностей.

Розробка API та фреймворків:

Коли ви створюєте бібліотеку або фреймворк, хочете надати розробникам «точки розширення». Інтерфейси ідеально підходять для цього. Ви можете сказати: «Якщо хочете, щоб ваш компонент працював із моєю системою, реалізуйте ось цей інтерфейс». Стандартні бібліотеки .NET містять багато інтерфейсів (наприклад, IEnumerable<T>, IDisposable, IComparable<T>) — вони визначають контракти для найпоширеніших сценаріїв.

Програмування на рівні інтерфейсів (Programming to an Interface):

Досвідчені розробники часто радять: «Програмуйте на рівні інтерфейсів, а не реалізацій». Це означає, що коли ви визначаєте тип змінної або параметра методу, замість конкретного класу (Car) краще використовувати інтерфейс (IDriveable). Це робить ваш код гнучкішим і менш залежним від деталей реалізації, дозволяючи легко замінювати одну реалізацію іншою.

5. Типові помилки при роботі з інтерфейсами

Помилка № 1: спроба створити примірник інтерфейсу.
Ви можете написати Cat murzik = new Cat("Мурзик", 3);, бо Cat — це конкретний клас. Але ви не можете написати ITalkable talker = new ITalkable();. Інтерфейс — це лише контракт, шаблон. Він не містить реалізації й не може бути створений безпосередньо. Це як креслення, а не готовий будинок.

Помилка № 2: забута реалізація всіх членів інтерфейсу.
Якщо ви вказали, що ваш клас реалізує інтерфейс, наприклад IMyInterface, то він зобов’язаний реалізувати всі його методи. Навіть один пропущений метод спричинить помилку компіляції: MyClass не реалізує IMyInterface.TheMissingMethod().

Помилка № 3: неправильні модифікатори доступу під час реалізації.
Методи інтерфейсу неявно public, і при реалізації вони теж мають бути public. Якщо спробувати зробити метод private або protected, компілятор видасть помилку. Обіцяли — реалізуйте відкрито.

Помилка № 4: спроба додати поля або конструктори до інтерфейсу.
Інтерфейси описують поведінку, а не стан. Тому не можна додавати до них поля або конструктори. Якщо спробуєте — отримаєте помилку компіляції. Дозволені лише властивості, і то — як опис гетерів/сетерів.

Помилка № 5: плутанина між override і реалізацією інтерфейсу.
Ключове слово override використовують для перевизначення методів базового класу. Але під час реалізації інтерфейсу воно не потрібне — просто оголошуйте метод із модифікатором public і потрібною сигнатурою. Це важливий нюанс, який легко пропустити.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ