JavaRush /Java блог /Java Developer /Принципи інкапсуляції в Java
Автор
Milan Vucic
Репетитор по программированию в Codementor.io

Принципи інкапсуляції в Java

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

}
У такій ситуації можна пильно стежити за тим, чи не створює хтось із твоїх колег об'єктів із неправильним станом, але набагато краще було б унеможливити саму можливість створювати такі "неправильні об'єкти". Принципи інкапсуляції - 4 З приховуванням даних нам допомагають:
  1. модифікатори доступу(private, protected, package default);
  2. геттери та сеттери.
Туди можемо, наприклад, закласти перевірку, чи не намагається хтось присвоїти коту від'ємне число як вік. Як ми казали раніше, автори різних статей про інкапсуляцію мають на увазі або інкапсуляцію (об'єднання даних і методів), або приховування, або і те, і інше. У Java присутні обидва механізми (в інших ООП-мовах це не обов'язково так), тож останній варіант буде найбільш правильним. Використання інкапсуляції дає нам кілька важливих переваг:
  1. Контроль за коректним станом об'єкта. Приклади цьому були вище: завдяки сеттеру і модифікатору private, ми убезпечили нашу програму від котів із вагою 0.
  2. Зручність для користувача завдяки інтерфейсу. Ми залишаємо «зовні» для доступу користувача тільки методи. Йому достатньо викликати їх, щоб отримати результат, і зовсім не потрібно заглиблюватися в деталі їхньої роботи.
  3. Зміни в коді не позначаються на користувачах. Усі зміни ми здійснюємо всередині методів. На користувача це не вплине: він як писав auto.gas() для газу машини, так і писатиме. А те, що ми поміняли щось у роботі методу gas(), для нього залишиться непомітним: він, як і раніше, просто отримуватиме потрібний результат.
Коментарі (3)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Василь Рівень 30
25 жовтня 2023
до речі, українською мовою у вас статті краще виходять!
Василь Рівень 30
25 жовтня 2023
дякую за статтю! Тільки я постійно плутаю інкапсуляцію і поліморфізм місцями?