1. Вступ

Сьогоднішню лекцію буде присвячено інкапсуляції. Ви вже в загальних рисах знаєте, що це таке.

Інкапсуляція

Які ж переваги надає інкапсуляція? Їх чимало, але я можу виділити чотири, які, на мою думку, є основними:


2. Валідний внутрішній стан

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

Тому об'єкт має стежити за змінами своїх внутрішніх даних, а краще — робити такі зміни самостійно.

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

Наприклад, ми хочемо, щоб усі могли дізнатися кількість елементів у нашій колекції, але ніхто не міг змінити це значення без нашого дозволу. Тоді ми оголошуємо змінну private int count і метод public getCount().

Якщо правильно використовувати інкапсуляцію, то жоден клас не зможе отримати безпосереднього доступу до внутрішніх даних нашого класу і, отже, не зможе змінити їх без нашого контролю. Це можна буде зробити тільки шляхом виклику методів того самого класу, де містяться змінювані дані.

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


3. Контроль переданих аргументів

Часом потрібно контролювати аргументи, що передаються в методи нашого класу. Скажімо, наш клас описує об'єкт «людина» і дозволяє вказати дату її народження. Нам потрібно перевіряти всі передані дані на відповідність логіці програми й логіці нашого класу. Наприклад, не допускати таких даних, як 13-й місяць, дата народження 30 лютого і т. ін.

Навіщо ж хтось указуватиме дату народження 30 лютого? По-перше, це може бути помилка користувача, який увів дані. По-друге, перш ніж програма почне працювати безперебійно, в ній доведеться виправити чимало помилок. Може статися, приміром, така ситуація.

Програміст пише програму, що визначає людей, день народження яких припадає на післязавтра. Припустімо, сьогодні 3 березня. Програма додає до сьогоднішнього дня в місяці число 2 й шукає всіх, хто народився 5 березня. На перший погляд усе правильно.

Але коли настане 30 березня, програма не знайде нікого, тому що 32 березня в календарі немає. У програмі буде менше помилок, якщо додати в методи перевірку переданих даних.

Згадайте, коли ми вивчали ArrayList і розбирали його код, там була перевірка індексу в методах get и set: index мав бути більшим за нуль або дорівнювати нулю і бути меншим за довжину масиву. Там іще викидався виняток, якщо в масиві не було елемента зі вказаним індексом. Це класичний приклад перевірки вхідних даних.


4. Як запобігти помилкам під час внесення змін в код класів

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

Цей клас виявився таким корисним, що ви вирішили його покращити. Але якщо ви видалите будь-який із методів цього класу, десятки людей не зможуть скомпілювати свої коди. Їм доведеться терміново все переписувати. А що більше змін, то більше помилок. Ви зіпсуєте купу збірок, і вас зненавидять.

А коли ми вносимо зміни в методи, оголошені як private, ми певні, що ніде немає жодного класу, який би викликав ці методи. Ми можемо їх переробити, змінити кількість параметрів і їхні типи, і залежний від них код працюватиме далі. Або принаймні компілюватиметься.


5. Указуємо спосіб взаємодії нашого об'єкта зі сторонніми об'єктами

Ми можемо обмежити можливі дії з нашим об'єктом. Наприклад, ми хочемо, щоб об'єкт можна було створити тільки в одному екземплярі, навіть якщо він створюється в декількох місцях проєкту одночасно. І ми можемо це зробити завдяки інкапсуляції.

Інкапсуляція 2

Інкапсуляція дає змогу вказувати додаткові обмеження, які можна перетворити на додаткові переваги. Наприклад, клас String реалізовано як незмінюваний (immutable) об'єкт. Об'єкт класу String є незмінюваним від моменту створення і до моменту смерті. Усі методи класу String (remove, substring, …), повертають новий рядок, нічого не змінюючи в об'єкті, для якого їх було викликано.

Інкапсуляція — це дуже цікава річ.