JavaRush /Курси /C# SELF /Інкапсуляція та модифікатори доступу

Інкапсуляція та модифікатори доступу

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

1. Вступ

Уявіть, що ви проєктуєте сучасний автомобіль. Усередині — сотні складних деталей, чутлива електроніка, тонкі налаштування. Очевидно, що ніхто не має просто так діставатися до блока керування двигуном і починати крутити якісь гвинтики. Якщо кожен втручатиметься там, де не слід, машина може поводитися непередбачувано, а вам доведеться влаштовувати техогляд із зʼясуванням, хто і що зламав.

Так само й у програмуванні. Коли ви створюєте клас, природно хотіти захистити його внутрішню будову від стороннього втручання — щоб ніхто випадково не зіпсував важливі дані або не втрутився в роботу методів, про які зовнішній світ не має знати. У цьому й полягає суть інкапсуляції — одного з трьох ключових принципів об’єктно-орієнтованого програмування.

Інкапсуляція дає змогу приховати деталі реалізації й чітко визначити, що доступне «ззовні», а що має залишатися всередині. Це як відкрити користувачеві речове відділення, але надійно замкнути капот. Клас сам вирішує, які дані показувати зовнішньому коду, а які — залишити в таємниці. Завдяки цьому код стає надійнішим, логічнішим і простішим у супроводі.

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

2. Модифікатори доступу: захищаємо власну територію

У C# (та інших об’єктно-орієнтованих мовах) інкапсуляцію реалізують за допомогою модифікаторів доступу. Це своєрідні «ярлики», які визначають, які члени класу (поля, методи, властивості тощо) доступні ззовні, а які — лише зсередини.

Ми вже з ними стикалися. Нагадаємо основне й трохи розширимо список модифікаторів:

Модифікатор Доступний…
public
Усім! Будь-якому коду в проєкті (і за його межами, якщо проєкт — бібліотека)
private
Лише зсередини цього ж класу
protected
Із цього класу та всіх його нащадків
internal
Лише в межах поточної збірки (проєкту)
protected internal
Або з поточної збірки, або з класу-нащадка
private protected
Лише з класу-нащадка всередині поточної збірки

У цій лекції зосередимося на найчастіше використовуваних: public, private і коротко зачепимо protected, щоб не перевантажувати матеріалом.

Відокремлюємо внутрішнє й зовнішнє

Напишімо клас Dog:


public class Dog
{
    public string Name;
    public int Age;

    public void Bark()
    {
        Console.WriteLine($"{Name} каже: Гав!");
    }
}

Тут усі поля та методи оголошені з модифікатором public. Це означає, що будь-хто може змінити імʼя або вік собаки:

Dog rex = new Dog("Рекс", 5);
rex.Name = "Шарик"; // Неочікувана зміна особистості!
rex.Age = -999;     // Серйозні проблеми з віком

Втім, поля Name і Age — це важливі дані, які не варто виставляти назовні без контролю.

Так робити не варто

Залишаючи всі поля public, ми ризикуємо порушити логіку роботи класу: будь-яке зовнішнє втручання може зробити об’єкт некоректним. Наприклад, задати собаці від’ємний вік або встановити їй імʼя на кшталт «%$#!??».

3. Ховаємо поля: використовуємо private

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


public class Dog
{
    private string name;
    private int age;

    public void Bark()
    {
        Console.WriteLine($"{name} каже: Гав!");
    }
}

Тепер спроба звернутися до полів безпосередньо ззовні:

Dog rex = new Dog("Рекс", 5);
rex.name = "Шарик";  // Помилка компіляції

Компілятор одразу скаже: «Немає доступу!».

Навіщо так жорстко? Де ж гнучкість?

Уся «гнучкість» забезпечується завдяки спеціальним методам або властивостям (про них — детальніше в наступній лекції), які дають змогу контролювати доступ і змінювати дані лише за певними правилами.

4. Інкапсуляція на практиці: приклад із контролем

Уявімо, що ми хочемо, щоб у собаки не можна було задати від’ємний вік:


public class Dog
{
    private string name;
    private int age;

    public Dog(string name, int age)
    {
        this.name = name;
        if (age >= 0)
            this.age = age;
        else
            this.age = 0; // Не даємо встановити некоректний вік
    }

    public void Bark()
    {
        Console.WriteLine($"{name} каже: Гав!");
    }

    // Метод для безпечної зміни віку
    public void SetAge(int newAge)
    {
        if (newAge >= 0)
            age = newAge;
        // Можна додати else: повідомлення про некоректне значення
    }
}

Тепер ніхто ззовні не може безпосередньо зіпсувати поля. Для зміни віку є спеціальний метод, що виконує необхідну перевірку.

5. Поля проти методів доступу (гетери/сетери)

Такий спосіб — робити поля private і надавати методи для роботи з ними — називають інкапсуляцією даних (data encapsulation). Для читання значення поля часто створюють гетери, а для запису — сетери.


public class Dog
{
    private string name;

    public Dog(string name)
    {
        this.name = name;
    }

    public string GetName()
    {
        return name;
    }

    public void SetName(string newName)
    {
        // Тут можна додати перевірку коректності імені
        name = newName;
    }
}

Але! З появою в C# властивостей (properties) такі методи використовують дедалі рідше — властивості роблять код значно чистішим і зручнішим (детальніше — у наступній лекції).

6. Модифікатори доступу для методів і класів

Поля — це далеко не все! Модифікатори доступу застосовують і до методів (функцій-членів класу), і навіть до самих класів.

Методи, які використовують лише всередині класу (наприклад, допоміжні функції для внутрішньої логіки), прийнято робити private.

Публічні методи — це так званий інтерфейс класу (не плутати з ключовим словом interface), тобто те, що доступне користувачу класу.

7. Типові помилки у роботі з інкапсуляцією

Помилка № 1: усе підряд оголошується як public.
Здається, що що більше відкрито, то простіше користуватися. Насправді це відкриває доступ до внутрішніх частин класу, які не повинні змінюватися ззовні. Такий код стає вразливим і непередбачуваним, особливо у великих проєктах.

Помилка № 2: зміна модифікатора доступу ламає зовнішній код.
Під час рефакторингу можна випадково змінити модифікатор доступу методу або поля, і тоді весь інший код, який із ними працював, раптово перестає компілюватися або поводиться неправильно. Особливо це критично для публічних API.

Помилка № 3: плутанина між локальними змінними та полями класу.
Іноді розробники забувають, що змінна, оголошена всередині методу, живе тільки в цьому методі. А поля класу — доступні у всіх його методах. Це призводить до неочевидних помилок, особливо якщо імена змінних збігаються.

Помилка № 4: нехтування private і protected.
Чимало розробників уникають обмеженого доступу, остерігаючись, що потім не зможуть звернутися до потрібного елемента. Але інкапсуляція якраз у цьому й полягає — ховати все зайве, а назовні виводити лише те, що справді потрібно.

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