Привіт! Сьогоднішню лекцію присвятимо інкапсуляції та почнемо її одразу з прикладів :)
Перед тобою —
звичайний автомат з газованою водою. У мене до тебе є питання: як він працює? Спробуй докладно відповісти: звідки випадає склянка, як підтримується температура всередині, де зберігається лід, як автомат розуміє, який сироп додати і т.д. Найімовірніше, відповідей на усі додаткові питання в тебе немає. Добре, можливо, не всі користуються такими автоматами, нині вони не настільки популярні. Спробуємо навести інший приклад. Що-небудь, чим ти точно користуєшся багато разів щодня. О, є ідея!
Розкажи, як працює
пошукова система 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(), для нього залишиться непомітним: він, як і раніше, просто отримуватиме потрібний результат.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ