Принципи ООП

Пропонуємо ознайомитися з основними принципами об’єктноорієнтованого програмування – однією з найважливіших методологій розробки, яка базується на представленні програми у вигляді сукупності об’єктів, кожен із яких є екземпляром певного класу, а класи утворюють ієрархію спадкування.

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

Одним із найважливіших факторів при проектуванні компонентів програми є приховування внутрішніх даних компонента та деталей його реалізації від інших компонентів програми та надання набору методів для взаємодії з ним (API). Цей принцип є одним із чотирьох фундаментальних принципів ОВП і називається інкапсуляцією .

Правильна інкапсуляція важлива з багатьох причин:

1. Вона сприяє перевикористання компонентів: оскільки в цьому випадку компоненти взаємодіють один з одним лише за допомогою їх API та байдужі до змін внутрішньої структури, вони можуть використовуватися у ширшому контексті.

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

3. Правильно інкапсульовані компоненти легші для розуміння та процесу налагодження, що спрощує підтримку програми.

У мові Java інкапсуляція реалізована за допомогою системи класів , які дозволяють зібрати інформацію про об’єкт в одному місці; пакетів , які групують класи за будь-яким критерієм, і модифікаторів доступу , якими можна помітити весь клас або його поле чи метод.

Усього модифікаторів доступу чотири:

  • public – повний доступ до сутності (поля чи методу класу) з будь-якого пакету;
  • protected – доступ до сутності лише для класів свого пакета та спадкоємців класу;
  • неявний модифікатор за промовчанням (за відсутності трьох явних) – доступ до сутності лише класів свого пакета;
  • private – доступ лише усередині класу, у якому оголошено сутність.

Для досягнення правильної інкапсуляції необхідно надати коректний API для роботи з компонентом. Наприклад, в сеттер для змінної можна включити логіку з перевірки значень, що передаються, або не надавати сеттери в класі зовсім, якщо клас повинен бути доступний тільки для читання.

Приклад коректної інкапсуляції класу:

У прикладі вище значення змінної name задається під час створення об’єкта і може бути змінено ззовні, оскільки сеттер для змінної відсутня. У сеттері для змінної age реалізована перевірка на коректність параметра, що передається, і викид виключення при неправильному значенні.

Успадкування

Спадкування є одним із найважливіших принципів об’єктно-орієнтованого програмування, оскільки воно дозволяє створювати ієрархічні структури об’єктів. Використовуючи успадкування, можна створити загальний клас, який визначатиме характеристики та поведінку, властиві якомусь набору пов’язаних об’єктів. Надалі цей клас може бути успадкований іншими, більш приватними класами, кожен з яких додаватиме унікальні, властиві тільки йому характеристики і доповнювати або змінювати поведінку базового класу. У термінах Java такий загальний клас називається суперкласом (superclass), або базовим класом (base class), або класом-батьком (parent class), а клас, що його успадковує, – підкласом (subclass), або дочірнім класом (child class), або класом-нащадком (derived class).

Спадкування реалізує ставлення «є» (“is-a”) між суперкласом та підкласом. Нехай, наприклад, класи Employee і Manager є абстракцією понять «Співробітник» та «Менеджер». Кожен менеджер також є співробітником компанії, в якій він працює, отже, клас Manager знаходиться у відношенні “is-a” із класом Employee. Таким чином, з точки зору спадкування, при вибудовуванні ієрархії класів клас Employee буде суперкласом, а клас Manager – дочірнім класом. При цьому клас, який є спадкоємцем якогось класу, може бути суперкласом для одного або кількох інших класів. Також на відміну, наприклад, від C++, Java в строгому сенсі відсутня множинне успадкування, тобто будь-який клас може мати не більше одного класу-батька. А всі класи, суперклас у яких явно не вказаний, успадковуються за умовчанням від класу Object.

Клас Employee у прикладі вище є суперкласом не тому, що він головніший за клас Manager або містить більше функціональності. Насправді вірно протилежне: функціональність підкласів не ýже, а часто значно ширше, ніж функціональність їхніх класів-батьків. Приставки “супер-” і “під-” прийшли в Java з математики: безліч всіх менеджерів міститься в багатьох співробітників і, таким чином, є підмножиною безлічі співробітників.

Для того, щоб успадкувати будь-який клас у Java, використовується ключове слово extends:

У прикладі вище клас Employee є базовим класом класу Manager, а клас Manager – підкласом класу Employee. Клас Employee абстрагує базові характеристики для всіх співробітників компанії – ім’я, прізвище, розмір окладу та дату прийому на роботу, а клас Manager доповнює ці характеристики відсотком премії для менеджерів та змінює поведінку методу getSalary() базового класу, використовуючи поліморфізм.

Поліморфізм

Під час розгляду поліморфізму слід пам’ятати, що це принцип нерозривно пов’язані з іншим принципом ОВП – успадкуванням, що допомагає реалізувати поліморфізм.

Візьмемо для прикладу абстрактний клас «Автомобіль», який успадковують два конкретні класи – «Спортивний автомобіль» та «Вантажний автомобіль».

І спортивні, і вантажні автомобілі матимуть спільні характеристики і матимуть можливість виконувати спільні для всіх автомобілів дії, які вказані в абстрактному класі-батьку, але конкретна реалізація цих дій може бути різною.

Наприклад, загальна для всіх автомобілів дія “завестися” у спортивного автомобіля може бути реалізована шляхом натискання кнопки, а у вантажного – за допомогою ключа. Один результат – різні рішення. У цьому полягає поліморфізм.

Суворіше, поліморфізм – одне із принципів ООП, що дозволяє викликом перевизначеного методу через змінну класу-батька отримати поведінка, яке відповідатиме реальному класу-нащадку, який посилається ця змінна.

Код вище є прикладом поліморфізму. На початку змінної батьківського класу Vehicle присвоюється об’єкт дочірнього класу SportCar. При виклику методу start() на консоль буде виведено: “Starting my fancy sport car!”

При подальшому присвоєнні цієї ж змінної об’єкта дочірнього класу Truck та виклику того ж методу start() на консоль буде виведено: “Starting my heavy truck!”

Абстракція

Відносно недавно як самостійний четвертий принцип почали виділяти абстракцію .

Одне з визначень слова «абстракція», які можна зустріти у сучасних словниках:

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

Всі мови програмування надають користувачеві певні абстракції. Так, мови сімейства асемблер є у своєму роді абстракцією відповідних мікропроцесорів, оскільки дозволяють відволіктися від деталей їх реалізації та спілкуватися з ними через певний набір більш високорівневих інструкцій. Імперативні мови програмування, що послідували за асемблером, наприклад Basic, Fortran, C, є вищим рівнем абстракції над асемблерними мовами – вони дають змогу використовувати звичні людині синтаксичні конструкції за рахунок наближення синтаксису до природних мов.

Об’єктно-орієнтовані мови, такі як Java, виводять розробку ще більш високий рівень абстракції: об’єкти в ООП за своєю суттю являють собою моделі понять навколишнього світу, таких як Працівник, Сервер, Запис у щоденнику, і виділяють тільки ті властивості цього поняття, які необхідні у разі для вирішення конкретної проблеми.

Наприклад, клас Student у додатку обліку студентів університету, крім загальних полів, таких як ім’я, прізвище, дата народження тощо, міститиме поля, що відображають інформацію про номер залікової книжки, статус студента (діючий, академічна відпустка, відрахований), факультет, номер його групи, оцінки за семестри і т.д. Але для того ж класу Student у додатку обліку студентів у тренінг-центрі EPAM така інформація буде неактуальною: клас міститиме поля, що відображають навчальний проект, на який був розподілений студент, рівень його англійської мови за результатами останнього тестування, кількість відвідуваних заходів тощо. буд.

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