Привіт! Сьогоднішню лекцію присвятимо інкапсуляції і почнемо її одразу з прикладів :)
Перед тобою – звичний автомат із газованою водою. У мене до тебе одне запитання: а як він працює? Спробуй відповісти докладно: звідки вивалюється склянка, як підтримується температура всередині, де зберігається лід, як автомат розуміє, який сироп додати тощо.
Найімовірніше, відповідей на ці запитання в тебе немає. Добре, можливо, не всі користуються такими автоматами, у нинішній час вони не настільки популярні.
Спробуємо навести інший приклад. Щось, чим ти точно користуєшся багато разів щодня. О, є ідея!
Розкажи, як працює пошуковик Google. Як саме він шукає інформацію за тими словами, які ти ввів? Чому нагорі знаходяться ці результати, а не інші?
Хоча ти користуєшся гуглом щодня, найімовірніше, ти цього не знаєш. Але це не важливо. Адже тобі й не потрібно цього знати.
Ти можеш вводити запити в пошуковик, не замислюючись, як саме він працює. Ти можеш купити газовану воду в автоматі, не знаючи, як він влаштований. Ти можеш водити машину, не заглиблюючись у роботу двигуна внутрішнього згоряння, і взагалі не знаючи фізики навіть на шкільному рівні.
Усе це можливо завдяки одному з головних принципів об'єктно-орієнтованого програмування – інкапсуляції.
Читаючи різні статті на цю тему, напевно ти стикався з тим, що в програмуванні є два поширених поняття – інкапсуляція і приховування. І під словом «інкапсуляція» автори розуміють то одне, то інше (так уже склалося). Ми розберемо обидва терміни, щоб у тебе було повне розуміння.
Початкове значення слова «інкапсуляція» в програмуванні – об'єднання даних і методів роботи з цими даними в одному пакуванні («капсулі»).
У Java в ролі пакування-капсули виступає клас. Клас містить у собі і дані (поля класу), і методи для роботи з цими даними.
Тобі це здається очевидним, але в інших концепціях програмування все влаштовано інакше. Наприклад, у функціональному програмуванні дані строго відокремлені від операцій над ними.
В ООП же (об'єктно-орієнтованому програмуванні) програми складаються з класів-капсул, які є одночасно і даними, і функціями для роботи з ними.
Тепер поговоримо про приховування.
Як же так виходить, що ми користуємося всілякими складними механізмами без розуміння, як вони влаштовані і на чому ґрунтується їхня робота? Усе просто: їхні творці надали простий і зручний інтерфейс.
На автоматі з газованою водою інтерфейс – це кнопки на панелі. Натиснувши одну кнопку, ти обираєш об'єм склянки. Натиснувши другу, вибираєш сироп. Третя відповідає за додавання льоду. І це все, що тобі потрібно зробити.
Неважливо, як саме автомат влаштований всередині. Головне – він влаштований так, що для отримання газованої води користувачеві потрібно натиснути три кнопки.
Те саме і з автомобілем. Неважливо, що там відбувається у нього всередині. Головне – під час натискання правої педалі автомобіль їде вперед, а під час натискання лівої – гальмує.
Саме в цьому полягає суть приховування. Усі "нутрощі" програми приховуються від користувача. Для нього ця інформація є зайвою, непотрібною. Користувачеві необхідний кінцевий результат, а не внутрішній процес.
Давай для прикладу подивимося на клас Auto:
З приховуванням даних нам допомагають:


public class Auto {
public void gas() {
/*всередині автомобіля відбуваються якісь складні речі
внаслідок яких він їде вперед*/.
}
public void brake() {
/*всередині автомобіля відбуваються якісь складні речі
в результаті яких він гальмує*/
}
public static void main(String[] args) {
Auto auto = new Auto();
//Як усе виглядає для користувача
//натиснув одну педаль – поїхав
auto.gas();
//натиснув іншу педаль – загальмував
auto.brake();
}
}
Ось як виглядає приховування реалізації в Java-програмі. Усе як у реальному житті: користувачеві надано інтерфейс (методи). Якщо йому потрібно, щоб автомобіль у програмі виконав дію, достатньо викликати потрібний метод. А вже що там відбувається всередині цих методів – інформація зайва, головне, щоб усе працювало як треба.
Тут ми говорили про приховування реалізації. Крім нього в Java є ще приховування даних. Про нього ми писали в лекції про геттери і сеттери, але не буде зайвим нагадати.
Наприклад, у нас є клас Cat:
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Мяу!");
}
}
Можливо, ти запам'ятав із минулої лекції, у чому проблема цього класу? Якщо ні – давай згадаємо.
Проблема в тому, що його дані (поля) відкриті для всіх, і інший програміст легко може створити в програмі безіменного кота з вагою 0 і віком – 1000 років:
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
У такій ситуації можна пильно стежити за тим, чи не створює хтось із твоїх колег об'єктів із неправильним станом, але набагато краще було б унеможливити саму можливість створювати такі "неправильні об'єкти".

- модифікатори доступу(private, protected, package default);
- геттери та сеттери.
- Контроль за коректним станом об'єкта. Приклади цьому були вище: завдяки сеттеру і модифікатору private, ми убезпечили нашу програму від котів із вагою 0.
- Зручність для користувача завдяки інтерфейсу. Ми залишаємо «зовні» для доступу користувача тільки методи. Йому достатньо викликати їх, щоб отримати результат, і зовсім не потрібно заглиблюватися в деталі їхньої роботи.
- Зміни в коді не позначаються на користувачах. Усі зміни ми здійснюємо всередині методів. На користувача це не вплине: він як писав auto.gas() для газу машини, так і писатиме. А те, що ми поміняли щось у роботі методу gas(), для нього залишиться непомітним: він, як і раніше, просто отримуватиме потрібний результат.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ