Рубріки: Думка

Стало масовим, але вже застаріло: п’ять найбільших проблем об’єктно-орієнтованого програмування

Оленка Пилипчак

Комп’ютери, які використовували для програмування у 1960-х роках, були не такі потужні як зараз. Ті незначні ресурси, які вони мали, потрібно було розподіляти між структурами даних та процедурами.  

Це означало, що, якщо у вас є багато даних, потрібно використати всю потужність комп’ютера, щоб щось з ними зробити. Але якщо це не єдине завдання, то треба обмежувати їх кількість, інакше процес обробки триватиме вічність. 

Потім у 1966 чи 1967 роках з’явився Алан Кей. Він запропонував використовувати інкапсульовані міні-комп’ютери, які обмінюються не даними, а повідомленнями. Так можна було заощадити обчислювальні ресурси.

Незважаючи на геніальність ідеї, об’єктно-орієнтоване програмування стало популярним не одразу. Але після 1981 року ситуація змінилась і уже кілька десятиріч воно приваблює як новачків, так і досвідчених програмістів. 

Проте останніми роками цю парадигму дедалі частіше критикують. Але чи насправді об’єктно-орієнтоване програмування застаріло?

Це питання ставить докторка наук з фізики елементарних частинок Ері Джурі. Також вона розробляє алгоритми штучного інтелекту, щоб краще ці частинки досліджувати.

Передаємо їй слово.

Невже поєднання функцій із даними — дурниця?

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

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

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

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

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

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

Варто зазначити, що успадкування та поліморфізм є не лише у об’єктно-орієнтованому програмуванні. Те, що його насправді відрізняє — це інкапсуляція фрагментів даних і методів, які до них належать. У 1960-ті це була просто геніальна ідея.

П’ять великих проблем об’єктно-орієнтованого програмування

Коли об’єктно-орієнтоване програмування стало масовим, воно трансформувало спосіб бачення коду розробниками. До 1980-х років у програмуванні переважало процедурне програмування, яке було дуже машинно-орієнтованим. Розробники мали досить багато знати про те, як працюють комп’ютери, щоб написати хороший код.

Завдяки інкапсуляції даних і методів об’єктно-орієнтоване програмування змістило акценти у розробці програмного забезпечення на людину. Саме людська інтуїція підкаже нам, що метод drive() належить до групи даних car, а не до групи teddybear.

Успадкування, яке з’явилося пізніше, також було інтуїтивно зрозумілим. Цілком логічно, що Hyundai є підгрупою автомобілів і має ті самі властивості, а PooTheBear ні.

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

1Проблема банана, горили та джунглів

Уявіть, що ви налаштовуєте нову програму та думаєте, як створити новий клас. І тут ви згадуєте про чудовий маленький клас, який зробили для іншого проєкту. Цей клас був би ідеальним і для нового. 

Жодних проблем! Ви можете повторно використовувати клас зі старого проєкту. Проте є один нюанс: цей клас може бути підкласом іншого класу. Тому вам потрібно буде додати і батьківський клас. А потім ви зрозумієте, що батьківський клас залежить від інших класів. В результаті ви мусите додавати купу коду.

Ось відома цитата творця Erlang, Джо Армстронга:

«Проблема з об’єктно-орієнтованими мовами полягає в тому, що вони є складним комплексом. Ви хотіли лише банан, але на додачу отримали горилу, яка його тримає, та джунглі, де вона живе».

Цим все сказано. Так, можливість повторно використовувати класи є чи не основною перевагою об’єктно-орієнтованого програмування. Але не варто використовувати її завжди: інколи краще написати новий клас. 

2Проблема крихкого базового класу

Уявіть, що ви успішно використали клас з іншого проєкту для свого нового коду. Що станеться, якщо базовий клас зміниться?

У результаті може постраждати весь ваш код, хоча ви нічого не змінювали у ньому.

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

Чим більше ви використовуєте успадкування, тим більше обслуговування доведеться робити. Тому, хоч повторне використання коду здається дуже ефективним у короткостроковій перспективі, у довгостроковій перспективі це може виявитись незручним.

3Діамантова проблема

Успадкування — чудова річ: з його допомогою ми можемо брати властивості одного класу та передавати їх іншим. Але що, якщо потрібно змішати властивості двох різних класів?

А ось це ви не можете зробити. Принаймні, у елегантний способ. Розглянемо, наприклад, клас Copier. (Я запозичила цей приклад, а також деяку інформацію про проблеми, що тут розглядаю, з тексту Чарльза Скальфані «До побачення, об’єктно-орієнтоване програмування»). Копіювальний апарат сканує вміст документа та друкує його на порожньому аркуші. Який це має бути підклас, сканера чи принтера?

Хорошої відповіді просто немає. І ця проблема виникає досить часто, щоб викликати занепокоєння у програмістів.

4Проблема ієрархії

У діамантовій проблемі питання було у тому, підкласом якого класу є Copier. Але я вам збрехала — є гарне рішення. Нехай Copier буде батьківським класом, а Scanner і Printer — підкласами, які успадковують лише підмножину властивостей. Проблему вирішено!

Але що відбуватиметься, якщо ваш копіювальний апарат не працює з кольором, а принтер може друкувати кольорові зображення? Чи не є принтер у цьому сенсі узагальненням копіювального пристрою? Що робити, якщо принтер підключено до Wi-Fi, а копіювальний пристрій — ні?

Чим більше властивостей у вашого класу, тим важче встановити правильну ієрархію. Ви маєте справу з кластерами властивостей, де Copier використовує деякі, але не всі властивості Printer і навпаки. Якщо вам потрібно визначити ієрархію у великому складному проєкті, це може призвести до безладу. 

5 Проблема взаємопов’язаності

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

Є лише одна проблема. Суть інкапсуляції полягає в тому, щоб убезпечити частини даних один від одного: так обчислення стають більш ефективними. А це не працює без суворої ієрархії.

Подумайте, що станеться, якщо об’єкт A перевизначить ієрархію, взаємодіючи з іншим об’єктом B. Немає значення, який зв’язок A має з B, за винятком того, що B не є прямим батьківським класом. Тоді A має містити приватне посилання на B, оскільки інакше він не міг би взаємодіяти.

Але якщо A містить інформацію, яку також мають підкласи B, то цю інформацію можна змінити в багатьох місцях. От і все: інформація про B більше не є безпечною, інкапсуляція не працює.

Хоча багато програмістів створюють програми з такою архітектурою, це не об’єктно-орієнтоване програмування. Це просто безлад.

Небезпека єдиної парадигми

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

Але не тільки з об’єктно-орієнтованим програмуванням можна перестаратися. У чистому функціональному програмуванні надзвичайно важко обробити введені користувачем дані або вивести повідомлення на екран. Для цього набагато краще підходить об’єктно-орієнтоване або процедурне програмування.

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

Парадигми трохи схожі на релігії. Так, можливо, Ісус, Магомет і Будда мали рацію у певних речах. Але якщо ви сліпо дотримуєтесь усіх релігійних приписів, то наврядче це принесе вам щастя. 

Так само і з парадигмами програмування. Зараз функціональне програмування стає популярнішим, а об’єктно-орієнтоване починають більше критикувати.

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

Головне питання: ми на порозі нової революції?

Дискусія щодо функціонального та об’єктно-орієнтованого програмування зводиться до наступного: чи справді ми спостерігаємо занепад об’єктно-орієнтованого програмування?

Часто функціональне програмування більш ефективне. Наприклад, для аналізу даних, машинного навчання, паралельного програмування. 

Але подивіться на вакансії: на десяток пропозицій для об’єктно-орієнтованих програмістів — лише одна для функціональних. Це не означає, що ви не отримаєте роботу, якщо віддаєте перевагу останньому; функціональних розробників у наші дні все ще досить мало.

Швидше за все, об’єктно-орієнтоване програмування буде використовуватись ще не один рік. Тож не поспішайте відмовлятися від нього, але звертайте увагу і на інші парадигми. 

Автор: Ері Джурі

Текст адаптувала Євгенія Козловська

Останні статті

Айтівець Міноборони США понабирав кредитів і хотів продати рф секретну інформацію

32-річний розробник безпеки інформаційних систем Агентства національної безпеки Джарех Себастьян Далке отримав 22 роки в'язниці…

30.04.2024

Простий та дешевий. Українська Flytech запустила масове виробництво розвідувальних БПЛА ARES

Українська компанія Flytech представила розвідувальний безпілотний літальний апарат ARES. Основні його переваги — недорога ціна…

30.04.2024

Запрошуємо взяти участь у премії TechComms Award. Розкажіть про свій потужний PR-проєкт у сфері IT

MC.today разом з Асоціацією IT Ukraine і сервісом моніторингу та аналітики згадок у ЗМІ та…

30.04.2024

«Йдеться про потенціал мобілізації»: Україна не планує примусово повертати українців із ЄС

Україна не буде примусово повертати чоловіків призовного віку з-за кордону. Про це повідомила у Брюсселі…

30.04.2024

В ЗСУ з’явився жіночий підрозділ БПЛА — і вже можна проходити конкурсний відбір

В Збройних Силах України з'явився жіночий підрозділ з БПЛА. І вже проводиться конкурсний відбір до…

30.04.2024

GitHub на наступному тижні випустить Copilot Workplace — ШІ-помічника для розробників

GitHub анонсував Copilot Workspace, середовище розробки з використанням «агентів на базі Copilot». За задумкою, вони…

30.04.2024