Парадигма программирования — это общий способ мыслить о программе и организовывать её код. Она подсказывает, что считать основными элементами решения: последовательность команд, объекты, функции, правила, события или потоки данных. Это не жёсткая инструкция и не синоним языка программирования.
Что такое парадигма простыми словами
Одну задачу можно описать по-разному. Например, чтобы получить список совершеннолетних пользователей, программист может написать цикл с изменяемым результатом, применить функцию фильтрации или сформулировать условие в запросе к базе данных. Итог похож, но способ выразить решение различается.
Парадигма влияет на несколько вещей:
- как представляются данные и состояние программы;
- как описываются действия и зависимости;
- на какие части делят систему;
- как код проверяют, расширяют и обсуждают в команде;
- какие ошибки легче предупредить.
Границы здесь условны. Одни учебники рассматривают процедурное и объектно-ориентированное программирование как разновидности императивного, другие выносят их в отдельные ветви. Спорить о единственно правильном дереве не очень полезно: важнее увидеть идеи и цену каждого выбора.
Язык и парадигма — не одно и то же
Язык даёт синтаксис и набор возможностей, а парадигма описывает стиль решения. Современные языки часто мультипарадигменны. JavaScript, например, поддерживает императивный, функциональный и объектно-ориентированный код. В одной программе можно использовать объекты для модели предметной области, функции для преобразования данных и обработчики для событий интерфейса.
Из возможностей языка стиль программы автоматически не следует. Наличие классов ещё не делает дизайн объектным, а один вызов map не превращает весь проект в функциональный.
Императивное программирование
В императивном стиле программа задаёт последовательность команд, которые меняют состояние. Разработчик описывает, как получить результат: создать переменную, пройти по элементам, проверить условие, обновить значение.
total = 0
for price in prices:
total = total + price
print(total)
Преимущество такого кода — явный порядок действий. Он удобен там, где важны пошаговые операции, управление ресурсами или тесное взаимодействие с системой. Минус проявляется при росте программы: если состояние меняется во многих местах, становится труднее проследить причины результата.
Процедурный подход
Процедурное программирование организует команды в процедуры и функции. Вместо одного длинного сценария код делят на именованные шаги с параметрами и результатами. Это распространённый способ сделать императивную программу понятнее и сократить повторения.
Объектно-ориентированное программирование
В объектно-ориентированном программировании систему представляют как взаимодействующие объекты, в которых соединены данные и поведение. Заказ может хранить позиции и уметь считать сумму, а корзина — добавлять товар и проверять ограничения.
Часто с ООП связывают:
- инкапсуляцию — объект управляет своим внутренним состоянием через заданный интерфейс;
- абстракцию — наружу выносятся существенные свойства, а детали реализации скрываются;
- полиморфизм — общий интерфейс допускает разные реализации;
- наследование — один тип расширяет другой, хотя композиция нередко даёт более простой дизайн.
Классы — популярный, но не единственный способ создавать объекты. JavaScript исторически использует прототипную модель, а синтаксис классов предоставляет удобную оболочку над ней. MDN отдельно отмечает, что выбор классов зависит от задачи: они помогают организовать код, но не обязаны быть основой каждой программы.
ООП удобно для предметных моделей и компонентов с ясными обязанностями. Но глубокие иерархии, изменяемое состояние и множество связей быстро превращают такую модель в сложный лабиринт.
Декларативное программирование
В декларативном стиле разработчик в первую очередь описывает требуемый результат или правило, а детали выполнения оставляет среде. SQL-запрос сообщает базе, какие строки нужны, но не перечисляет все операции чтения и сортировки:
SELECT name
FROM users
WHERE age >= 18
ORDER BY name;
Декларативный код не означает «код без алгоритма» и не гарантирует отсутствие состояния внутри системы. Планировщик базы данных всё равно выполняет конкретные шаги. Просто эти шаги не задаются пользователем запроса напрямую.
К декларативным подходам обычно относят функциональное и логическое программирование, языки запросов и многие предметно-ориентированные описания. HTML описывает структуру документа, а CSS — правила оформления, но HTML сам по себе обычно не называют языком программирования.
Функциональное программирование
Функциональный стиль строит вычисления из функций и выражений. Функции можно передавать как значения, комбинировать и применять к коллекциям. Особое внимание уделяют предсказуемым функциям и ограничению изменяемого состояния.
const adultNames = users
.filter(user => user.age >= 18)
.map(user => user.name);
Чистая функция при одинаковых аргументах возвращает одинаковый результат и не меняет внешнее состояние. Такой код проще проверять и безопаснее выполнять параллельно. На практике приложения взаимодействуют с сетью, файлами, временем и интерфейсом, поэтому побочные эффекты не исчезают — их стараются отделять от вычислений.
Функциональное программирование не сводится к рекурсии и не запрещает состояние как факт. Главный вопрос — где состояние хранится, кто может его изменить и можно ли проследить изменения.
Логическое программирование
В логическом программировании задают факты, правила и запрос. Система ищет значения, при которых утверждения выполняются. Классический пример — Prolog:
parent(anna, olga).
parent(olga, ivan).
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
Запрос к правилу может найти пары бабушек, дедушек и внуков. Подход используют в задачах поиска, вывода и представления знаний, хотя для обычного веб-интерфейса он выбирается реже.
Событийное и реактивное программирование
Событийная программа реагирует на действия пользователя, сетевые ответы, сообщения очереди и другие события. Обработчик кнопки в браузере — простой пример. Этот признак пересекается с другими парадигмами: обработчик можно написать и императивно, и функционально.
Реактивный подход описывает распространение изменений по зависимостям или потокам данных. Когда источник выдаёт новое значение, зависящие от него вычисления обновляются. Реактивность полезна в интерфейсах и системах обработки потоков, но требует аккуратно управлять временем жизни подписок, ошибками и порядком событий.
Параллельное и конкурентное программирование
Конкурентность описывает работу с несколькими задачами, которые продвигаются в перекрывающиеся периоды. Параллелизм означает фактическое одновременное выполнение. Это скорее отдельная ось проектирования, чем ветвь только императивного или декларативного кода.
Для таких программ важны модели взаимодействия: общая память и блокировки, передача сообщений, акторы, асинхронные функции, потоки данных. Выбор модели влияет на гонки данных, отказоустойчивость и отладку.
Где находится обобщённое программирование
Обобщённое программирование отделяет алгоритм или структуру данных от конкретного типа. Например, функция сортировки может работать с числами, строками и пользовательскими объектами, если задан способ сравнения. В C++ для этого применяют шаблоны, в Java и C# — обобщённые типы.
Его часто называют парадигмой или стилем, но не стоит ставить его в одно строгое дерево с императивным и декларативным программированием. Обобщение типов можно применять внутри процедурного, объектного и функционального кода.
Сравнение основных подходов
| Подход |
Главный вопрос |
Характерный инструмент |
Пример |
| Императивный |
Какие шаги выполнить? |
Команда и изменение состояния |
Цикл с накоплением суммы |
| Процедурный |
Как разделить шаги на операции? |
Процедуры и функции |
Набор функций обработки файла |
| Объектно-ориентированный |
Какие объекты взаимодействуют? |
Объекты, интерфейсы, композиция |
Модель заказа и оплаты |
| Функциональный |
Как составить вычисление из функций? |
Функции как значения, неизменяемые данные |
Цепочка фильтрации и преобразования |
| Логический |
Какие факты и правила должны выполняться? |
Правила и запросы |
Поиск связи в базе знаний |
| Событийный |
Как реагировать на событие? |
Обработчики и очередь событий |
Интерфейс браузера |
Как выбрать парадигму
Выбор обычно задают задача, экосистема и опыт команды:
- для последовательной обработки удобно начать с простого процедурного кода;
- для модели с сущностями и устойчивыми обязанностями полезны объекты и композиция;
- для цепочек преобразований данных подходят функциональные приёмы;
- для запроса к данным — декларативный язык вроде SQL;
- для интерфейса или сервиса с внешними сигналами нужна событийная модель;
- для множества одновременно работающих задач важнее явно выбрать модель конкурентности.
В рабочем проекте эти подходы почти всегда соседствуют. Их стоит оценивать по понятности зависимостей, контролю состояния, удобству тестирования и цене следующего изменения, а не по терминологической «чистоте».
Типичные ошибки
- Считать язык одной парадигмой. Большинство популярных языков позволяют смешивать несколько стилей.
- Приравнивать ООП к классам. Важнее границы ответственности и взаимодействие объектов.
- Считать декларативный код бесшаговым. Шаги выполняются, но их выбирает среда.
- Называть любую функцию функциональным программированием. Функции есть и в процедурном коде.
- Строить единственную строгую классификацию. Событийность, конкурентность и обобщение пересекают основное деление.
- Переписывать проект ради модного термина. Парадигма должна снижать сложность конкретной системы.
Как изучать парадигмы на практике
- Возьмите небольшую задачу: фильтрацию списка, каталог товаров или очередь заданий.
- Сначала решите её обычным циклом и функциями.
- Затем перепишите преобразование без изменения исходной коллекции.
- Смоделируйте предметную часть несколькими объектами, избегая глубокой иерархии.
- Сформулируйте выборку данных как SQL-запрос.
- Сравните не число строк, а понятность состояния, тесты и цену нового требования.
Так различия становятся видны в коде, а не остаются списком определений.
Источники