Переглядаючи приклади використання принципів ООП, я неодноразово помічав, що їх розуміють невірно. Або ж це була просто відсутність практики, що дозволила б навести відповідний приклад. В цій статті я розгляну основні помилки, які допускали кандидати на інтернатуру в своїх прикладах, а також дам поради стосовно того, як краще підійти до створення прикладів з ООП.
Інкапсуляція
Найбільша помилка в розумінні того, що таке інкапсуляція – це вважати що це можливість зробити поля приватними і додати до кожного з них по get і set методу відповідно. Концептуально це мало чим відрізняється від класу з набором публічних полів. Це тісно перекликається з поняттями об’єкту та структури, що детально розглядаються в розділі Objects and Data Structures книги Clean Code Роберта Мартіна. Розглянемо простий приклад.
package com.in6k.incamp.oop.bad; public class Car { int speed; public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } }
В цьому прикладі ми ніяк не приховуємо стан об’єктів класу Car, тобто його швидкість. Вона може бути змінена в будь-який момент на будь-яке значення, що не відповідає умовам поводження машин в реальному світі. Ми не можемо одразу змінити швидкість з нуля до 250 км/годину, мати від’ємну швидкість або різко зменшити швидкість до нуля. Розглянемо інший приклад.
package com.in6k.incamp.oop.good; public class Car { private static final int SPEED_STEP = 30; private static final int MAX_SPEED = 300; private int speed = 0; public void accelerate() { if (speed >= MAX_SPEED) return; this.speed += SPEED_STEP; } public void brake() { if (speed == 0) return; this.speed -= SPEED_STEP; } public int getSpeed() { return speed; } }
В цьому прикладі тільки сам об’єкт може змінювати свій стан, при цьому перевіряючи поточні умови, що б не допустити свого перебування в некоректному стані. Клієнти цього класу можуть змінювати швидкість автомобіля через відповідні методи прискорення та гальмування а також отримувати поточну швидкість. Але вони не мають можливості змінити безпосередню швидкість автомобіля на будь-яке значення. Таким чином клас приховує деталі своєї реалізації та інкапсулює свій стан.
Наслідування
Одною з проблем, що я бачив в багатьох прикладах, була неправильна ієрархія класів. Наприклад, клас співробітника, що наслідується від компанії. Звісно, такий приклад невірний – співробітник не є компанією.
Інший невдалий приклад – це наслідування різних брендів або моделей авто від класу автомобіль. Так, BMW X6 це автомобіль, але в цілому виділення для нього окремого класу не має сенсу. Цей клас ніяк не конкретизує відомості, які можуть бути вказані про будь-яке авто. В даному випадку правильним є вказати окреме поле, що буде містити інформацію про модель автомобіля.
Інколи створюють спеціальні класи лише з метою перевизначити в них конструктор для того, щоб вказати інші значення для полів за замовчування. Проте такий підхід лише ускладнює ієрархію класів та збільшує час на її аналіз. Більш простим та зрозумілим є впровадження фабричних методів, які відповідають за створення об’єктів з відповідними налаштуваннями.
Наслідування може використовуватись з двох причин:
- Повторне використання структури класу та поведінки у випадку, коли це наслідування відповідає зв’язку “is a” або “являється”.
- Як засіб вказати, що сам клас або його похідні реалізовують конкретний інтерфейс взаємодії з ними.
Повторне використання коду
Правильне використання наслідування з метою повторного використання коду можливе, якщо екземпляри дочірніх класів так само “є” екземплярами батьківського. Наприклад, як студент, який вчиться на певному курсі та отримає оцінки з предметів, так і викладач, що читає декілька предметів та отримає зарплатню, обидва мають ім’я, прізвище, вік та контактні данні і є людьми (здебільшого). Детальні рекомендації з правильного використання наслідування ви можете дізнатись з принципу підстановки Лісков.
Наслідування інтерфейсу
Інша можлива мета використання наслідування – це успадкування інтерфейсу. Наприклад, ми можемо мати інтерфейс Drawable, який різні фігури, або об’єкти що з них складаються, можуть реалізовувати різним чином.
Поліморфізм
Щодо поліморфізму в мене є декілька порад:
- З точки зору програмного коду для виводу статичного тексту на консоль поліморфізм не потрібен. Я не раз бачив ієрархію класів, єдина задача яких виводити статичний текст – не цікаво.
- Перевантаження методів різними аргументами в контексті ООП не є поліморфізмом. Точніше, це не той поліморфізм, який ми маємо на увазі, коли говоримо про ООП.
- Демонстрація його використання має проявлятися в клієнтському коді (хоча б в Main класі) за умови, що клієнт не знає з екземплярами яких класів він має справу.
Найбільш простим та поширеним прикладом поліморфізму є розрахунок площі фігур. І тут варто пам’ятати, що метод має виконувати лише одну задачу – просто розраховувати площу і повертати її значення. Не виводи на консоль, не записувати у поле об’єкту – просто повертати. Докладніше про організацію методів читайте у розділі Functions вже відомої вам книги Clean Code.
Чому перевантаження методів то є не той дроідполіморфізм, якого ми шукаємо? Тому що задача поліморфізму позбавити клієнта необхідності знати, з екземплярами яких конкретних класів він має справу і тим самим досягти різних варіантів поведінки. В результаті, в нас можуть з’являтися нові класи в ієрархії, а клієнтський код буде працювати і надалі, без необхідності його змінювати чи компілювати.
А от перевантаження методів – то є лише ілюзія поліморфізму, більш зручний метод написання коду. Вже на етапі компіляції компілятор знає, який метод має бути використаний. А якщо він того не дізнається, то і не зможе зібрати програму. У випадку з поліморфізмом, фактичний метод, який має бути викликаний, визначається вже процесі робити програми.
Підводячи підсумки
Сподіваюсь, в цій стислій статті мені вдалось розкрити вам основні недоліки в розумінні принципів ООП та їх використання, з якими я найчастіше мав справу. Також я раджу переглянути розділ Object Oriented Model, де ви знайдете більше деталей та прикладів використання принципів ООП. Успіхів вам у навчанні, і не забувайте про практику!
Post A Reply