Программирование  •  18 января  2023  •  5 мин чтения

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

Какие бывают парадигмы программирования и как их можно использовать? Объясняем на примере тыквенного супа.

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

Разработчики определили и структурировали подходы к решению задач. Они объединили их в понятные правила и назвали парадигмами. Языки программирования конструируются на основе этих правил. У каждого языка есть свой набор парадигм, которым следуют при разработке.

Ещё парадигмами называют запрет на определённые действия внутри кода программы. Его придумал Роберт Мартин — международный консультант в области разработки, известный среди разработчиков как дядя Боб. С его точки зрения, парадигмы — это ограничения на определённые языковые конструкции, которые вынуждают использовать определённый стиль. Например, процедурное программирование накладывает запрет на прыжки по коду программы, а функциональное — на прямое изменение памяти компьютера.

Одна и та же задача может быть решена по-разному: каждая парадигма подразумевает своё решение

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

Две основные парадигмы, императивная и декларативная, включают в себя другие. Например, к императивной парадигме относится процедурное и объектно-ориентированное программирование (ООП), а к декларативной — функциональное и логическое.

Попробуем приготовить тыквенный суп, используя основные парадигмы программирования.

Материал по теме:
Что такое рекурсия в программировании и почему про неё постоянно шутят

Императивная парадигма

Императивная — это самая простая и часто используемая парадигма. Её смысл в последовательном выполнении действий.
Следуя этой парадигме, алгоритм приготовления тыквенного супа можно описать так
Императивной парадигмы придерживаются десятки языков, у неё много разновидностей. Раньше большинство программистов писало на процедурных языках, а теперь процедурное программирование можно назвать устаревшим. Им пользуются, только если по какой-то причине нельзя использовать объектно-ориентированное программирование, которое занимает основную нишу в императивной вселенной и относится к современным парадигмам.
Процедурное программирование
Примеры языков: C, Pascal, COBOL, ALGOL, BASIC, Fortran.

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

Когда гость приходит в ресторан и открывает меню, каждая позиция там — это определённая функция
Гость зовёт официанта и сообщает название блюда, а именно — какую функцию нужно исполнить. При этом он не думает, какие конкретно действия должен выполнить официант, ему важен результат. Точно так же при вызове функции программист не обязан думать о том, какие конкретно действия она совершит для получения запрошенного результата. Достаточно знать название функции и уметь к ней обратиться.

В своё время процедурное программирование произвело революцию, вытеснив такой язык, как Assembler, но сейчас оно ушло на покой, уступив место объектно-ориентированному.

Объектно-ориентированное программирование
Примеры языков: Java, Python, C++, Ruby, C#, Objective-C, PHP.

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

Объектно-ориентированное программирование позволяет регулировать связи между частями программы, которые отвечают за разные действия. За счёт этого программу легче разделить между разработчиками, проще поддерживать и легче автоматически протестировать.

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

Декларативная парадигма

Декларативная парадигма требует от программиста ответа на вопрос «что должно получиться?», а не «что нужно сделать?». Компьютер ищет способ получить требуемый результат.
Удобнее всего было бы просто попросить компьютер приготовить суп и дождаться, когда он всё сделает сам. Но пока что компьютер так не умеет, поэтому нужно дать пояснение
Если при декларативном программировании слишком размыто обозначить результат, то компьютер может начать делать не то, что нужно. Например, вместо полного описания разработчик просто сообщил, что нужен тыквенный суп. Компьютер начинает перебирать все существующие элементы и смешивать их. При таком подходе сначала может получиться куриный суп, затем грибной, и так до тех пор, пока программа не подберёт подходящие ингредиенты для тыквенного. С точки зрения декларативной парадигмы это абсолютно корректный подход — ведь в итоге он помогает достичь нужного результата. Но он совсем не эффективный, ведь будет потрачено много продуктов, денег и времени.

Георгий Осипов
В программировании очень легко допустить неочевидные ошибки, которые сложно заметить, потому что они не проявляются до поры до времени. Например, у разработчика, тестировщика и клиента всё работает, но в один прекрасный момент в банке при определённой транзакции пропадает миллиард долларов. Будет трудно найти причину. Поэтому в программировании очень важно не только написать программу и убедиться, что она работает, но и тщательно проверить её корректность. Если чётко следовать парадигмам, то у разработчика будет меньше возможностей ошибиться, а значит, и программы, которые он напишет, будут более надёжными.

Функциональное программирование
Примеры языков: Haskell, Erlang, Scala, F#, OCaml, ELM, Lisp.

К современной декларативной парадигме прежде всего относится функциональное программирование. В строгом функциональном подходе считается, что все функции чистые и не имеют побочных эффектов. Гость может заказать хоть тысячу раз тыквенный суп, и каждый раз официант принесёт ему одно и то же блюдо. Результат функции всегда будет одинаковым. Но в реальном ресторане на десятый раз закончились бы сливки 20% и их бы заменили на 10%, на двадцатый — закончилась бы и тыква, а функция стала бы выдавать ошибку «невозможно приготовить тыквенный суп». Это и есть побочный эффект — то, что изменилось в мире при работе функции, о чём пользователь может не знать.

Чтобы решить эту проблему, при функциональном подходе нельзя просто заказать суп, а нужно предъявить все продукты, необходимые для приготовления. То, что от них останется, будет возвращено в качестве результата вместе с самим супом.

На кухне готовили пюре и суп. В рецепт внесли изменение — заменили соль в пюре, но забыли про побочный эффект
Минус побочных эффектов в том, что их сложно учитывать. Предположим, повар готовит два блюда — картофельное пюре и тыквенный суп. Он отмеряет для них 20 грамм соли. При приготовлении пюре расходуется половина соли — 10 грамм. Оставшиеся 10 грамм повар оставляет на столе, и это становится побочным эффектом приготовления пюре. Затем повар готовит тыквенный суп и добавляет в него остаток соли со стола. Позже оказывается, что шеф решил внести в рецепт изменение — теперь нужно использовать в пюре сванскую соль вместо обычной. На столе остаётся 20 грамм соли вместо десяти, которые повар насыпает в суп, и в итоге получает пересоленное блюдо. Также и в программировании: побочные эффекты трудно учитывать при изменении программы.

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

Сравнение императивной и декларативной парадигмы

Если программист разрабатывает GUI приложения, где кнопка подсвечивается при наведении мыши, то в императивном стиле потребуется обработать два события: наведение курсора и уход мыши с элемента.
При наведении мыши понадобится перекрасить фон, нарисовать обводку и, возможно, изменить стиль надписи. Когда пользователь уберёт курсор, то все эти действия нужно будет проделать в обратном порядке. Если дизайнер предложит поменять внешний вид, то придётся переписывать оба действия и всегда следить за тем, чтобы они были согласованы. Так обстоит дело при императивном подходе
В декларативном программировании нужно просто описать, как будет выглядеть элемент в обычном состоянии и когда на него наведена мышь. Компьютер должен сам определить момент, когда нужно перерисовать элемент и как именно это нужно сделать. При этом несложно добавить предусмотренные языком эффекты, например анимации и плавные переходы.

Функциональное программирование подходит не для любых задач. С его помощью сложно написать игру в жанре «стратегия в реальном времени». В таких играх юниты, или игровые персонажи, перемещаются по игровому полю. Перемещение одного юнита — это изменение поля, а в строгом функциональном стиле изменения запрещены. Можно только создать полностью новую вселенную с новыми позициями игроков. Эта операция, скорее всего, потребует столько вычислительных ресурсов, что игра будет заметно тормозить. Современные функциональные языки имеют обходные пути для решения подобных задач, но для некоторых алгоритмов их трудно найти, если работать только в рамках функционального стиля.

Примеры использования

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

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

3. Декларативная парадигма отлично подходит для описания внешнего вида веб-сайтов.
Сейчас для этого используется декларативное программирование на языке CSS (от англ. Cascading Style Sheets — «каскадные таблицы стилей»). С его помощью задают шрифт текста, цвет фона, размер картинки.

4. Функциональная парадигма используется в криптовалютах и блокчейне.
Это области, в которых надёжность выдвигается на первый план, они новые и открыты для экспериментальных технологий. Другие функциональные языки стирают грань между математикой и программированием, позволяя буквально программировать математические доказательства. В основе этого соответствие Карри — Ховарда — математический факт, который гласит, что программа на функциональном языке и доказательство — это одно и то же. Его можно использовать для формальной верификации — строгой автоматической проверки корректности программ.

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

Совет эксперта

Георгий Осипов

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

Смысл не только в том, чтобы программа работала и выдавала красивый результат, но и в том, чтобы она была написана правильно. Если она будет красивой внутри, то другие разработчики её поймут, смогут оценить и дополнить.

Статью подготовили:

Георгий Осипов
ВМК МГУ
Программист лаборатории компьютерной графики и мультимедиа
Яндекс Практикум
Редактор

Дайджест блога: ежемесячная подборка лучших статей от редакции

Поделиться

Успейте начать учебу в Практикуме до конца ноября со скидкой 20%

Thu Oct 10 2024 18:44:56 GMT+0300 (Moscow Standard Time)