Стало масовим, але вже застаріло: п’ять найбільших проблем об’єктно-орієнтованого програмування
Комп’ютери, які використовували для програмування у 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 більше не є безпечною, інкапсуляція не працює.
Хоча багато програмістів створюють програми з такою архітектурою, це не об’єктно-орієнтоване програмування. Це просто безлад.
Небезпека єдиної парадигми
Ці проблеми виникають тоді, коли успадкування застосовують там, де це не найкраще рішення. Оскільки успадкування не було в початковій ідеї цієї парадигми, я б не казала, що це проблеми лише об’єктно-орієнтованого програмування.
Але не тільки з об’єктно-орієнтованим програмуванням можна перестаратися. У чистому функціональному програмуванні надзвичайно важко обробити введені користувачем дані або вивести повідомлення на екран. Для цього набагато краще підходить об’єктно-орієнтоване або процедурне програмування.
Тим не менш, є розробники, які не розуміють це. Вони пишуть десятки рядків коду, які ніхто не може зрозуміти. Хоча, якщо б вони використали іншу парадигму, то з легкістю скоротили б код до кількох читабельних рядків.
Парадигми трохи схожі на релігії. Так, можливо, Ісус, Магомет і Будда мали рацію у певних речах. Але якщо ви сліпо дотримуєтесь усіх релігійних приписів, то наврядче це принесе вам щастя.
Так само і з парадигмами програмування. Зараз функціональне програмування стає популярнішим, а об’єктно-орієнтоване починають більше критикувати.
Цікавтеся новими парадигмами програмування та використовуйте їх, коли це доречно. Якщо об’єктно-орієнтоване програмування є тим молотком, який змушує розробників бачити цвяхи, куди б вони не пішли, чи є це причиною викидати молоток у вікно? Ні. Ліпше покласти до своєї скриньки з інструментами викрутку та пару ножиць і обирати інструмент, дивлячись на проблему, яку потрібно вирішити.
Головне питання: ми на порозі нової революції?
Дискусія щодо функціонального та об’єктно-орієнтованого програмування зводиться до наступного: чи справді ми спостерігаємо занепад об’єктно-орієнтованого програмування?
Часто функціональне програмування більш ефективне. Наприклад, для аналізу даних, машинного навчання, паралельного програмування.
Але подивіться на вакансії: на десяток пропозицій для об’єктно-орієнтованих програмістів — лише одна для функціональних. Це не означає, що ви не отримаєте роботу, якщо віддаєте перевагу останньому; функціональних розробників у наші дні все ще досить мало.
Швидше за все, об’єктно-орієнтоване програмування буде використовуватись ще не один рік. Тож не поспішайте відмовлятися від нього, але звертайте увагу і на інші парадигми.
Автор: Ері Джурі
Текст адаптувала Євгенія Козловська
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: