Розвиток мов, стилів та технологій програмування
Ранні мови програмування
Перші електронні обчислювальні машини (ЕОМ) виникли відносно недавно – у 40-ві роки XX століття. Слідом за цим виникли й перші мови програмування, які були досить примітивні і орієнтовані на числові розрахунки. Це були і суто теоретичні наукові розрахунки (математичні й фізичні), і прикладні завдання, в першу чергу у галузі військової справи.
Програми, написані на ранніх мовах програмування, були лінійними послідовностями елементарних операцій з регістрами, в яких зберігалися дані. Тому будь-що технологія програмування була відсутня. Перші кроки в розробленні технології полягали у представленні програми у вигляді послідовності операторів – так званий операторний підхід. Написанню послідовності машинних команд передувало складання операторної схеми, що відбивала послідовність операторів і переходи між ними. У цей період почало зароджуватися поняття алгоритму.
Потрібно відзначити, що ранні мови програмування були оптимізовані під ту апаратну архітектуру конкретного комп’ютера, для якого вони призначалися, і хоча вони забезпечували високу ефективність обчислень, ніякої стандартизації ще не було. Програма, що була цілком працездатною на одній обчислювальній машині, часто не могла бути виконана на іншій. Таким чином, ранні мови програмування істотно залежали від середовища обчислень і приблизно відповідали сучасним машинним кодам або мові асемблера. Перші мови програмування часто називають мовами низького рівня.
Час появи: 1940 роки.
Стисла характеристика: лінійна послідовність елементарних інструкцій «низького рівня».
Переваги: висока обчислювальна ефективність.
Недоліки: істотна залежність від середовища обчислень. Приклади: машинні коди, асемблери.
Імперативне програмування (Imperative programming)
Приблизно у 50-ті роки XX століття з’явилися мови програмування так званого «високого рівня» порівняно з раніше розглянутими попередниками. Різниця від мов низького рівня полягає у підвищенні ефективності праці розробників за рахунок абстрагування від конкретного апаратного забезпечення. Один оператор мови високого рівня відповідав послідовності з кількох низькорівневих команд. Виходячи з того, що программа фактично являла собою набір директив, звернених до комп’ютера, такий підхід до программування назвали імперативним. Можна вважати що в цей період з’явились стилі, або парадигми, програмування. На рис.1 наведена схема їх розвитку.
Наступним кроком розвитку програм стало підвищення їх структурованості – структурний підхід, при якому виділяли канонічні структури: лінійні ділянки, цикли та розгалуження. Завдяки цьому з’явилася можливість читати і перевіряти програму як текст, а це підвищило ефективність праці програмістів під час розроблення та відлагодження програм.
Процедурний підхід (Procedural programming) та імперативні мови програмування
Розмір програм постійно збільшувався, тому програмісти почали об’єднувати окремі їх частини у підпрограми, якігрупували у бібліотеки. Бібліотеки підключалися до основної робочої програми, яка за необхідності викликала потрібну підпрограму. Фактично такий підхід збільшив структурність програм – велика програма стала сукупністю процедур- підпрограм. Одна підпрограма, головна, розпочинала роботу всієї програми. Тобто відбувся перехід до наступного етапу розвитку технологій – процедурного програмування. При розробленні окремої процедури для використання інших процедур потрібно було знати лише їх призначення та спосіб. Процедурний підхід дозволив скоротити затрати праці і машинного часу на створення і модернізацію програм завдяки можливості зміни окремої процедури без втручання в інші. З’явилася можливість повторного використання раніше написаних програмних блоків завдяки їх ідентифікації і наступного звернення за цим ідентифікатором. Також завдяки структурності програм була збільшена їх надійність – підпрограми стали зв’язуватись одна із одною тільки шляхом передачі їм аргументів, змінні розподілилися на локальні і глобальні. Окрім того, поява мов високого рівня значно зменшила залежність реалізації від апаратного забезпечення. Щоб це реалізувати, були створені спеціалізовані програми – транслятори, призначені для перетворення інструкції мови програмування у коди певної машини. Використання трансляторів привело до певної втрати швидкості обчислень, але цей недолік компенсувався значним виграшем у швидкості розроблення і модифікації програм. Також у цей період розпочали розроблення спеціалізованих мов програмування для розв’язання конкретних класів задач: для систем керування базами даних, імітаційного моделювання та ін. У той час програми все частіше розроблялися для виконання завдань військового призначення, для космічної галузі, енергетики – і від надійності програмного забезпечення залежали людські життя. Одним із напрямків удосконалення мов програмування стало підвищення рівня типізації даних. Використання жорстко типізованої мови при розробленні програми дозволяє ще під час її трансляції у машинні коди виявити більшість помилок використання даних і цим підвищити якість програми. Але типізація обмежує свободу програміста і не дозволяє виконувати частину перетворень даних, що часто потрібно у системному програмуванні. Практика програмування показала, що велика частина високорівневих мов, створених у період процедурного підходу, дуже вдало реалізована, тому вони або їх варіації використовуються до цього часу. Наприклад, до цього часу використовується мова Fortran для реалізації обчислювальних алгоритмів, мова COBOL для опису бізнес-процесів або мова APL, що поетапно трансформувалась у мову С (Cі). Потреба підвищення рівня типізації мов програмування привела до появи мови Pascal (Паскаль). Одночасно з Паскалем була розроблена мова С, що здебільшого орієнтована на системне програмування і є слабко типізованою мовою.
Час появи: 1950 роки.
Стисла характеристика: програма – послідовність інструкцій-операторів, у яку включені блоки типових дій – процедури або функції.
Переваги:
- підвищення рівня абстракції;
- менша машинна залежність;
- більша сумісність;
- змістовна значущість текстів програм;
- уніфікація програмного коду;
- підвищення ефективності праці програмістів.
Недоліки:
- більші витрати на вивчення мов;
- менша обчислювальна ефективність.
Приклади: Fortran, ALGOL, PL/1, APL, BPL, COBOL, Pascal, C, Basic.
Декларативне програмування (Declarative programming)
У 60-х рр. ХХ століття виникає новий підхід до програмування, що до цього часу успішно конкурує з імперативним, а саме – декларативний підхід. При декларативному підході до програмування програма є не набором команд, а описом дій, які потрібно виконати. Цей підхід легко формалізується математичними засобами. У результаті програми легше перевіряти на помилки і відповідність технічним специфікаціям (верифікувати). Також даний підхід має високий ступінь абстракції. Фактично програміст оперує не набором інструкцій, а абстрактними поняттями, часто досить узагальненими. На початку декларативним мовам програмування було важко конкурувати з імперативними у зв’язку із проблемами при створенні ефективних трансляторів. Програми працювали повільніше, однак вони вирішували більш абстрактні завдання із меншити затратами. Наприклад, мова SML була розроблена як засіб доведення теорем. Діалекти мови LISP виникли завдяки тому, що ця мова є ефективною під час обробки символьної інформації.
Функціональне програмування (Functional programming)
Одним із шляхів розвитку декларативного стилю програмування став функціональний підхід, що виник із появою і розвитком мови LISP. Відмінною особливістю даного підходу є та обставина, що будь-що програма, написана такою мовою, може інтерпретуватися як функція з одним або кількома аргументами, деякі з яких також можна розглядати як функції. Складні програми при такому підході будуються за допомогою поєднання функцій. Такий підхід дає можливість прозорого моделювання тексту програм математичними засобами. При функціональному програмуванні повторне використання коду зводиться до виклику раніше описаної функції, структура якої, на відміну від процедури імперативної мови, прозора математично. Більш того, типи окремих функцій, що використовуються у функціональних мовах, можуть бути змінними. Тобто забезпечується можливість обробки різнорідних даних (наприклад, упорядкування елементів списку за зростанням для цілих чисел, окремих символів і рядків), або поліморфізм. Завдяки реалізації механізму зіставлення із зразком, такі мови, як ML та Haskell, дуже добре застосовні для символьної обробки. Ще однією важливою перевагою реалізації мов функціонального програмування є автоматизований динамічний розподіл пам’яті комп’ютера для зберігання даних. При цьому програміст позбавляється від рутинного обов’язку контролювати дані. До недоліків мов функціонального програмування відносять нелінійну структуру програми і відносно невисоку ефективність реалізації. Однак перший недолік досить суб’єктивний, а другий успішно подоланий сучасними реалізаціями, зокрема низкою останніх трансляторів мови SML, включаючи і компілятор для середовища Microsoft. NET.
Час появи: 1960 роки.
Стисла характеристика: програма – функція, аргументи якої, можливо, також є функціями.
Переваги:
- повністю автоматичне управління пам’яттю комп’ютера («збирання сміття»);
- простота повторного використання фрагментів коду;
- розширена підтримка функцій з параметричними аргументами;
- простота верифікації і тестування програм;
- строгість математичної формалізації;
- високий ступінь абстракції, абстрагування від машинного представлення даних;
- прозорість реалізації рекурсивних функцій;
- зручність символьної обробки даних (списки, дерева).
Недоліки:
- складність ефективної реалізації; необхідність фундаментальних математичних знань;
- нелінійна структура програми;
- відносно низька ефективність.
Приклади: LISP (Interlisp, Common Lisp, Scheme), SML, Haskell, Prolog, Miranda, Hope.
Логічне програмування (Logic programming)
У 70-х рр. ХХ століття виникла ще одна гілка мов декларативного програмування, пов’язана з проектами у галузі штучного інтелекту – мови логічного програмування. Згідно з логічним підходом до програмування програма є сукупністю правил або логічних висловлювань. Мови логічного програмування базуються на класичній логіці і застосовні для систем логічного висновку, зокрема для так званих експертних систем. На мовах логічного програмування, природно, формалізується логіка поведінки, їх можна застосовувати для описів правил прийняття рішень, наприклад, у системах, орієнтованих на підтримку бізнесу. Важливою перевагою підходу є досить високий рівень машинної незалежності, а також можливість відкотів – повернення до попередньої мети при негативному результаті аналізу одного з варіантів у процесі пошуку рішення (скажімо, чергового ходу при грі в шахи), що позбавляє від необхідності пошуку рішення повним перебором варіантів і збільшує ефективність реалізації. Одним з недоліків логічного підходу в концептуальному плані є специфічність класу вирішуваних завдань. Інший недолік практичного характеру полягає у складності ефективної реалізації для прийняття рішень у реальному часі, скажімо, для систем життєзабезпечення. Як приклади мов логічного програмування можна навести Prolog (назва виникла від слів PROgramming in LOGic) і Mercury.
Час появи: 1970 роки.
Стисла характеристика: програма – сукупність правил або логічних висловлювань з причиною і наслідком.
Переваги:
- високий рівень абстракції;
- зручність програмування логіки поведінки;
- зручність застосування для експертних систем;
- механізм відкотів (backtrack).
Недоліки:
- обмежене коло завдань;
- нелінійна структура програми;
- недостатньо ефективна реалізація.
Приклади: Prolog, Mercury.
Об’єктно-орієнтоване програмування (Object-oriented programming)
Усі універсальні мови програмування, незважаючи на відмінності у синтаксисі і використовуваних ключових словах, реалізують одні й ті самі канонічні структури: оператори присвоювання, цикли і розгалуження. У всіх сучасних мовах наявні базові типи даних (цілі і речові арифметичні типи, символьний і, можливо, рядковий тип), є можливість використання агрегатів даних, у тому числі масивів і структур (записів). Разом з тим при розробленні програми для розв’язання конкретної прикладної задачі потрібна як можна більша близькість тексту програми до опису завдання. Одним із шляхів вирішення цієї проблеми було створення розширюваної мови, що містить невелике ядро і допускає розширення, доповнює мову типами даних і операторами, відбиває концептуальну сутність конкретного класу задач.
Розвитком цього підходу і стало об’єктно-орієнтоване програмування (ООП) – стиль програмування, що фіксує поведінку реального світу таким способом, при якому деталі його реалізації приховані. У рамках даного підходу програма є описом об’єктів, їх властивостей (або атрибутів), сукупностей (або класів), відносин між ними, способів їх взаємодії та операцій над об’єктами (або методів). Механізм успадкування атрибутів і методів дозволяє будувати похідні поняття на основі базових і таким чином створювати модель як завгодно складної предметної області із заданими властивостями. Ще однією важливою властивістю ООП є підтримка механізму обробки подій, які змінюють атрибути об’єктів і моделюють їх взаємодію у предметній області. Переміщаючись по ієрархії класів від більш загальних понять предметної області до більш конкретних і навпаки, програміст отримує можливість змінювати ступінь абстрактності погляду на модельований ним реальний світ. Використання раніше розроблених (можливо, іншими колективами програмістів) бібліотек об’єктів і методів дозволяє значно заощадити трудовитрати при виробництві програмного забезпечення, особливо типового. Об’єкти, класи і методи можуть бути поліморфними, що робить реалізоване програмне забезпечення більш гнучким і універсальним. Складність адекватної (несуперечливої і повної) формалізації об’єктної теорії породжує труднощі тестування та верифікації створеного програмного забезпечення. Ця обставина є одним з найбільш істотних недоліків ООП. Найбільш відомим прикладом об’єктно-орієнтованої мови програмування є мова C++, що виникла з імперативної мови С. Її логічним продовженням є мова С#.
Час появи: 1970 роки.
Стисла характеристика: програма – опис об’єктів, їх сукупностей, відносин між ними і способів їх взаємодії.
Переваги:
- інтуїтивна близькість до довільної предметної області;
- моделювання як завгодно складних предметних областей;
- подієва орієнтованість;
- високий рівень абстракції;
- повторне використання описів;
- параметризація методів обробки об’єктів.
Недоліки:
- складність тестування та верифікації програм.
Приклади: C ++, Visual Basic, C #, Eiffel, Oberon.
Подієво-кероване програмування (Event-driven programming)
Розвитком об’єктно-орієнтованого підходу став перехід до подієво-керованої концепції у 90-х рр. ХХ століття і виникнення цілого класу мов програмування, які отримали назву мов сценаріїв або скриптів. У рамках даного підходу програма є сукупністю можливих сценаріїв обробки даних, вибір яких ініціюється настанням тієї чи іншої події (клік по кнопці мишки, перехід курсора в ту чи іншу позицію, зміна атрибутів того чи іншого об’єкта, переповнення буфера пам’яті та ін.). Події можуть ініціюватися як операційною системою, так і користувачем.
Час появи: 1990 роки.
Коротка характеристика: програма – сукупність описів можливих сценаріїв обробки даних.
Переваги:
- інтуїтивна зрозумілість;
- близькість до предметної області;
- високий ступінь абстракції;
- можливість повторного використання коду;
- сумісність з інструментальними засобами автоматизованого проектування (CASE) і швидкого розроблення (RAD) прикладного програмного забезпечення.
Недоліки:
- складність тестування та верифікації програм;
- множинні побічні ефекти.
Приклади: VBScript, PowerScript, LotusScript, JavaScript.
Паралельні обчислення (Parallel computing)
Ще одним дуже важливим класом мов програмування є мови підтримки паралельних обчислень. Програми, написані на цих мовах, є сукупністю описів процесів, які можуть виконуватися як одночасно, так і в псевдопаралельному режимі. В останньому випадку пристрій, що обробляє процеси, функціонує в режимі поділу часу, виділяючи час на обробку даних, що надходять від процесів, у міру необхідності (а іноді з урахуванням послідовності або пріоритетності виконання операцій).
Час появи: 1980 роки.
Стисла характеристика: програма – сукупність описів процесів, які можуть виконуватися одночасно або псевдопаралельно.
Переваги:
- висока обчислювальна ефективність для великих програмних систем (тисячі одночасно працюючих користувачів або комп’ютерів);
- висока ефективність функціонування в системах реального часу (системи життєзабезпечення та прийняття рішень).
Недоліки:
- висока собівартість розроблення невеликих програм (сотні рядків коду);
- відносно вузький спектр застосування.
Приклади: Ada, Modula-2, Oz.
Компонентне програмування (Component-based programming)
Компонентне програмування – парадигма програмування, що виникла як набір певних обмежень, що накладаються на механізм об’єктно-орієнтованого програмування, коли стало зрозуміло, що безконтрольне застосування ООП приводить до виникнення проблем з надійністю великих програмних комплексів. Компонентне програмування визначає набір правил та обмежень, спрямованих на побудову великих програмних систем, здатних до розвитку впродовж тривалого життєвого циклу. При компонентному програмуванні програмна система складається із окремо створених елементів (компонентів), які викликають один одногой через інтерфейси. Зміни в існуючу систему вносяться додаванням нових компонентів або заміною існуючих. При цьому нові компоненти, які заміняють раніше створені, повинні наслідувати інтерфейси базового.

Рисунок 1 – Стилі програмування