У сучасному світі розробки програмного забезпечення існує дві основні парадигми програмування: імперативна та декларативна. Вони являють собою різні підходи до написання комп’ютерних програм. Розглянемо, які мови програмування до них відносяться і як використовуються.
Декларативні та імперативні мови програмування
Імперативні мови
Це мови, що відповідають на питання, як побудована програма та що конкретно потрібно зробити для отримання результату. До цієї категорії можна віднести процедурне та об’єктно-орієнтоване програмування (ООП). Серед мов, що належать до цієї групи:
- Java
- JavaScript
- C#
- C++
- PHP та багато інших.
Під час розробки застосунку ми пишемо послідовно, що саме ми хочемо отримати. Якщо плануємо сформувати звіт з якихось даних, то доведеться прописати, де ці дані потрібно взяти, хто виступає джерелом зберігання інформації, в якому форматі має бути фінальний звіт, яку бібліотеку використовувати. Це досить складний процес, на який програмісти витрачають багато часу. Слід зазначити, що більшість програм написані саме в імперативному стилі.
Декларативні мови
Декларативні ж мови не зосереджуються на принципах побудови ПЗ: для них важливий тільки результат. Типовими представниками цієї парадигми є функціональне, логічне програмування та мова бази даних (наприклад, SQL). Розглянемо, як працюють такі мови програмування, на прикладі. Ми подаємо запит на отримання потрібних нам даних, але не пишемо, як саме це буде відбуватися, тобто не уточнюємо, яка взаємодія має виникнути між серверною і клієнтською частинами. У кожній базі даних є потужний двигун, який опрацьовує цей запит і видає результат.
Комбіновані стилі
Не завжди програмісти можуть залишатися в рамках одного визначеного підходу. Для досягнення найкращих результатів розробники часто використовують комбіновані стилі. Створені таким способом програми отримали назву мультипарадигмальних. Та все ж таки 90% розробки здійснюється за допомогою ООП.
У таблиці нижче можна ознайомитися з детальною класифікацією перерахованих вище мов програмування:
Процедурне програмування | C, Pascal, Golang |
ООП | C++, Java, C# |
Бази даних | SQL, PL/SQL |
Функціональне програмування | Erlang, Haskell, Lisp |
Логічне програмування | Prolog, Mercury, Parlog |
Мультипарадигмальні мови | Java, C#, Python, JS |
Процедурний підхід у програмуванні
Процедурний стиль програмування – підхід до написання програмного коду, що базується на використанні процедур (або функцій, або підпрограм) для структурування програми та її логіки. В основі процедурного програмування лежать такі основні принципи:
- модульність
- локалізація даних
- послідовність виконання.
Однак процедурний стиль програмування також включає можливість зміни стану всієї програми через спільний доступ до глобальних змінних або структур даних. Хоча глобальні змінні можуть полегшити обмін даними між процедурами, вони також можуть призвести до проблем зі стабільністю програми та її підтримкою.
Використання процедурного програмування допомагає забезпечити кращу структурованість коду, що сприяє зрозумілості та легкості його підтримки. Але він може стикатися з обмеженнями, особливо коли йдеться про великі та складні проекти. В таких випадках інші підходи, такі як об’єктно-орієнтоване програмування, можуть бути більш слушними.
Приклад програми, написаної в процедурному стилі:
Тобто в нас є програма і багато функцій, які агрегують в собі якусь поведінку. Вона виконується лінійно з використанням циклів, розгалужень та інших конструкцій мов програмування. Відповідно, ми отримуємо досить багато рядків коду і зосереджуємося на тому, як нам щось зробити. Для цього потрібно розбити задачу на невеликі фрагменти, які потім можна перевикористовувати для досягнення цілей.
Зверніть увагу на те, що при такому підході саме з перевикористанням часто виникають проблеми, тому що є можливість зміни стану всієї програми через спільний доступ до глобальних змінних або структур даних.
Об’єктно-орієнтоване програмування
Об’єктно-орієнтоване програмування (ООП) – це стиль програмування, який базується на концепції “об’єктів” та їх взаємодії. Об’єкти є екземплярами класів, які представляють сутності або концепції у програмі. Класи визначають атрибути (дані) та методи (функції), які об’єкти можуть використовувати для виконання певних дій.
Якщо у попередньому прикладі програма була написана лінійно, то в ООП впроваджено такий підхід:
- спочатку потрібно визначитися зі структурою – це так званий клас, на основі якого ми можемо створювати об’єкт;
- кожен об’єкт повинен містити якісь властивості;
- щоб змінювати поведінку об’єкта, тобто маніпулювати властивостями, існують методи (їх ще можна назвати функціями чи процедурами);
- в залежності від певних подій, ми можемо викликати методи й отримувати потрібні результати.
Так виглядатиме програма, яка у попередньому прикладі була написана за допомогою процедурного підходу, в ООП:
За такого підходу всі логічні фрагменти розділені, агреговані в класи й за допомогою методів наділені певною поведінкою. Таким чином, можливість перевикористання коду значно вища. Програма, спроектована з використанням ООП, буде значно краща з точки зору масштабування та перевикористання. Ще одна її особливість полягає в тому, що дані завжди відділені від логіки: є доменні класи, в яких зберігаються дані, і сервісні класи, які маніпулюють доменними для виконання якоїсь логіки.
Принципи ООП:
- Інкапсуляція. Кожен клас має бути добре інкапсульований. Це означає, що властивості класу доступні лише самому класу. Щоб зробити їх загально доступними, потрібно надати модифікатори доступу. Це дозволяє змінювати властивості даного класу без зміни інших фрагментів програми. Можна сказати, що інкапсуляція – це приховування даних.
- Поліморфізм. Це одна з основних властивостей, яка наділяє програму динамічною поведінкою за допомогою перевизначення базової поведінки класу, яку ми можемо успадкувати у пращура в класі нащадку. Це спрощує роботу з об’єктами та дозволяє використовувати їх узагальнено, незалежно від їх конкретного типу.
- Наслідування. Це можливість створювати нові класи на основі існуючих, успадковуючи їх атрибути та методи. Це полегшує повторне використання коду та дозволяє створювати більш загальні та спеціалізовані класи.
ООП допомагає розробникам створювати більш гнучкі, модульні та легкі для розуміння та підтримки програми. Це досягається шляхом структурування коду за допомогою об’єктів та класів, що відображають реальні сутності та їх взаємодію. ООП забезпечує кращі можливості для модуляції, абстракції та повторного використання коду.
Таким чином, об’єктно-орієнтоване програмування допомагає розробникам створювати програми, які легше адаптуються до змін, розширюються та підтримуються у майбутньому. Воно дозволяє структурувати програму таким чином, що кожен компонент відповідає за конкретну функціональність та має чітко визначений інтерфейс для взаємодії з іншими компонентами.
Функціональний стиль програмування
Функціональне програмування – це підхід до написання коду, сфокусований на використанні функцій та немутабельності даних, відходячи від змінного стану та побічних ефектів.
Основні принципи функціонального програмування:
- Чисті функції. Функції, що не впливають на зовнішній стан і не залежать від нього. Чисті функції використовуються для створення нових значень на основі вхідних аргументів, не змінюючи змінних або об’єктів.
- Функції вищого порядку. Функції, що приймають інші функції як аргументи або повертають їх як результат. Це дозволяє виконувати загальні операції та композицію функцій.
- Рекурсія. Функціональне програмування часто використовує рекурсію замість ітерацій для повторення операцій, коли функція викликає сама себе з новими аргументами до досягнення базового випадку.
Функціональний підхід пропонує ряд переваг, таких як простота розуміння, легкість аналізу та відлагодження коду, відсутність побічних ефектів, легкість тестування та рефакторингу. Такий стиль програмування сприяє створенню модульних та паралельних програм з ефективним використанням ресурсів.
Функціональне програмування може бути застосоване у багатьох мовах програмування, включаючи спеціалізовані (Haskell, Lisp, Clojure, Erlang) та загального призначення (Python, JavaScript, Ruby, Java) мови. Однак варто враховувати, що функціональний стиль може бути не оптимальним для задач, де потрібно часто відстежувати стан.
Компільовані та інтерпретовані мови програмування
Окрім відношення до двох парадигм, усі мови програмування поділяються на компільовані та інтерпретовані.
Компільовані мови
Почнемо з того, що таке компіляція програми. Це перетворення коду, написаного програмістом високорівневою мовою, на машинний код, який зрозумілий операційній системі та обладнанню, де він буде виконуватись. Для кожної конкретної платформи – Windows, Linux, MacOS, AndroidOS та ін. – потрібно компілювати програму.
Програміст пише логіку обраною мовою – наприклад, C++. Якщо він захоче, щоб ця програма працювала на Linux, її потрібно скомпілювати. Тобто перетворити C++ на машинний код, який буде виконаний ОС Linux. Сюди ж будуть запаковані всі бібліотеки, потрібні для виконання програми. Якщо провести аналогію з Windows, то будь-який .exe файл – це скомпільована програма з усіма бібліотеками, що потрібні для її запуску.
Переваги компільованих програм:
- Швидкість виконання програми. Програма, яка скомпільована в машинний код, перевершує швидкість інтерпретованої програми в десятки й сотні разів.
- Вища якість коду. Якщо ми запустили компіляцію, а в одному з рядків коду є помилка, компіляція не відбудеться.
Є у таких програм і недоліки:
- Необхідність пошуку компілятора для кожної платформи. Деякі програми не достатньо просто скомпілювати, їх потрібно переписувати. Тобто про кросплатформність у більшості компільованих програм доведеться забути.
- Складність внесення змін до вихідного коду. При внесенні змін до вихідного коду потрібно виконати компіляцію всієї програми, щоб побачити ці зміни в її роботі. Це може тривати декілька годин, що незручно, особливо якщо зміни мінімальні та їх потрібно вносити часто.
Прикладами компільованих мов програмування є Java, C#, C++.
Інтерпретовані мови
Як альтернатива компільованим мовам програмування нещодавно з’явилися інтерпретовані мови. Серед них – JavaScript, Python, PHP та інші. Особливість у тому, що для них неважлива платформа, на якій буде працювати програма. Вони концентруються на тому, щоб програма могла запускати вихідний код. Причому робити це миттєво. Інтерпретатор – це і є та програма, яка, з одного боку, приймає написаний програмістом вихідний код, а з іншого – перетворює його на машинний код, зрозумілий системі.
Як приклад – звичайні браузери, кожен з яких є інтерпретатором, скомпільованим під різні операційні системи. Саме він і дозволяє працювати програмам, фронтенд частина яких написана інтерпретованими мовами програмування (наприклад, JavaScript).
Переваги інтерпретованих програм:
- Кросплатформність. Вони будуть працювати на будь-якій платформі, де є відповідний інтерпретатор.
- Менший об’єм коду. У порівнянні з компільованими програмами, інтерпретовані будуть не такими громіздкими.
- Надання всіх необхідних бібліотек інтерпретатором. Не потрібно замислюватися над тим, які бібліотеки необхідні для запуску програми.
Їхні недоліки:
- Обов’язкова наявність програми-інтерпретатора. Інтерпретоване ПЗ не може працювати самостійно.
- Низька продуктивність. Інтерпретовані програми завжди будуть програвати компільованим у швидкості.
- Відсутність перевірки на синтаксичну коректність. Ми можемо написати 1000 рядків коду, і все буде працювати в браузері. Та натиснувши на певну кнопку, ми можемо дізнатися про помилку, присутню в передостанньому рядку. Для раннього виявлення дефектів потрібно використовувати додаткові спеціалізовані інструменти.
Якщо зобразити роботу компільованих та інтерпретованих програм схематично, це буде виглядати так:
Умовно компільовані мови програмування
Окрім перерахованих вище, існують ще й умовно компільовані мови програмування. До таких відносять, наприклад, Java і C#. Розглянемо цей тип на прикладі Java.
Враховуючи всі недоліки та переваги компільованих та інтерпретованих мов, розробники прийшли до висновку, що може бути ефективно поєднати ці 2 підходи. Тобто використати плюси компільованих мов, але запускати їх на платформі за допомогою інтерпретаторів. У розрізі Java це буде виглядати таким чином:
У нас є .java файл і скомпільований .class файл. Компіляція у цьому випадку відбувається не в машинний платформозалежний код, а у так званий bytecode, зрозумілий віртуальній машині JVM – своєрідному інтерпретатору. Такі програми є компільованими до цього моменту. Коли віртуальні машини починають їх виконувати, вони стають інтерпретованими. Це допомагає пришвидшити процес компіляції та впевнитися, що така програма не містить синтаксичних помилок. Також завдяки цьому підходу отримуємо кросплатформову мову програмування.
До інших переваг таких програм можна віднести:
- Високу швидкість роботи. Умовно компільовані програми працюють в десятки, а то і сотні разів швидше, ніж інтерпретовані. Це можливе внаслідок того, що на етапі компіляції відбувається оптимізація, тобто покращення коду.
- Відсутність прив’язки до платформи. З операційною системою взаємодіє віртуальна машина.
Недоліками умовно компільованого підходу можна вважати те, що User Interface (UI, або інтерфейс користувача) створений за допомогою JVM, виглядає дещо громіздким, а також неможливість використання платформозалежних функцій.
Можна зробити висновок, що недоліки та переваги існують у кожного підходу до розробки програмного забезпечення. Однак сьогодні умовно компільовані мови програмування впевнено завойовують ринок, і навіть чисті інтерпретовані мови намагаються запровадити такий підхід.
Радимо вам наш технічний словник бізнес-аналітика, для того, щоб найголовніша інформація завжди була під рукою.
Краще орієнтуватись в технічній термінології вам допоможе курс Online Technical Skills for PMs and BAs.