Все статьи
Программирование Редакционная политика

Что такое парадигмы программирования

Объясняем на простых примерах, чем различаются императивный, объектно-ориентированный, функциональный и другие подходы к коду.

Автор
Проверил эксперт/редактор
Ольга Миронова Ольга Миронова Главный редактор
Что такое парадигмы программирования

Парадигма программирования — это общий способ мыслить о программе и организовывать её код. Она подсказывает, что считать основными элементами решения: последовательность команд, объекты, функции, правила, события или потоки данных. Это не жёсткая инструкция и не синоним языка программирования.

Что такое парадигма простыми словами

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

Парадигма влияет на несколько вещей:

  • как представляются данные и состояние программы;
  • как описываются действия и зависимости;
  • на какие части делят систему;
  • как код проверяют, расширяют и обсуждают в команде;
  • какие ошибки легче предупредить.

Границы здесь условны. Одни учебники рассматривают процедурное и объектно-ориентированное программирование как разновидности императивного, другие выносят их в отдельные ветви. Спорить о единственно правильном дереве не очень полезно: важнее увидеть идеи и цену каждого выбора.

Язык и парадигма — не одно и то же

Язык даёт синтаксис и набор возможностей, а парадигма описывает стиль решения. Современные языки часто мультипарадигменны. 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;
  • для интерфейса или сервиса с внешними сигналами нужна событийная модель;
  • для множества одновременно работающих задач важнее явно выбрать модель конкурентности.

В рабочем проекте эти подходы почти всегда соседствуют. Их стоит оценивать по понятности зависимостей, контролю состояния, удобству тестирования и цене следующего изменения, а не по терминологической «чистоте».

Типичные ошибки

  • Считать язык одной парадигмой. Большинство популярных языков позволяют смешивать несколько стилей.
  • Приравнивать ООП к классам. Важнее границы ответственности и взаимодействие объектов.
  • Считать декларативный код бесшаговым. Шаги выполняются, но их выбирает среда.
  • Называть любую функцию функциональным программированием. Функции есть и в процедурном коде.
  • Строить единственную строгую классификацию. Событийность, конкурентность и обобщение пересекают основное деление.
  • Переписывать проект ради модного термина. Парадигма должна снижать сложность конкретной системы.

Как изучать парадигмы на практике

  1. Возьмите небольшую задачу: фильтрацию списка, каталог товаров или очередь заданий.
  2. Сначала решите её обычным циклом и функциями.
  3. Затем перепишите преобразование без изменения исходной коллекции.
  4. Смоделируйте предметную часть несколькими объектами, избегая глубокой иерархии.
  5. Сформулируйте выборку данных как SQL-запрос.
  6. Сравните не число строк, а понятность состояния, тесты и цену нового требования.

Так различия становятся видны в коде, а не остаются списком определений.

Источники

Содержание