Что такое парадигма в информатике
Парадигмы программирования — парадигмы жизни
Введение
Парадигма программирования
Ну что же, приступим. Скорее всего, многим данное словосочетание покажется пугающим, но, на самом же деле, здесь нет ничего сложного. Что же такое “Парадигма программирования”? Возьмем определение из википедии: “Парадигма программирования – это совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию). Это способ концептуализации, определяющий организацию вычислений и структурирование работы, выполняемой компьютером”.
Если говорить самым простым языком, то парадигма программирования – это подход к написанию кода. Читатели без опыта программирования, вероятнее всего, зададутся вопросами: “А что это за такие подходы? То есть, например, ты пишешь код сначала левой рукой, потом правой? Или же ты пишешь код час, а потом полчаса отдыхаешь? Что это за подходы?!”. На самом же деле, подходы подразумевают под собой совсем другое и далеки от примеров, описанных до этого.
Чтобы понять, что это такое, давайте рассмотрим два основных подхода, то есть две основные парадигмы: Объектно-ориентированное программирование (ООП) и Функциональное программирование (ФП). ООП основано на том, что все можно представить в виде объектов, у которых могут быть какие-то характеристики (поля) и они могут выполнять какие-то действия (методы/функции). ФП же основано на описание процессов (функций), а не объектов. Понимаю, пока не понятно, поэтому давайте разберем простейший пример. Представим, что нам нужно как-то складывать и вычитать два числа (a и b). На картинке ниже представлен код, реализующий это в ООП и ФП (код был специально упрощен, чтобы сделать упор именно на разнице двух подходов). Вы сразу можете заметить функции “sum” (сложение) и “subtract” (вычитание), которые мы хотели реализовать.
— Так, ну функции я вижу, а почему в ООП есть еще “class Calculator”, что это значит?
Как уже было сказано выше, ООП основано на том, что все можно представить в виде объектов: например, есть объект “Вселенная”, она состоит из галактик (объект “Галактика”), та, в свою очередь, состоит из планет (объект “Планета”), на каждой планете могут быть люди (объект “Человек”) и так далее. Возьмем, например, объект “Человек”, его характеристиками (полями) могут быть: цвет кожи, пол, рост, возраст (что-то еще). И также он может выполнять следующие действия (методы/функции): бегать, кушать, работать и так далее. Вы легко можете спроецировать такой подход (парадигму) на жизнь, представить все вокруг с помощью объектов и вы поймете, что любое действие (метод/функция) связано с каким-либо объектом, оно не может существовать само по себе, кто-то должен его выполнять. Именно по этой причине наши действия (методы/функции) “sum” и “subtract” находятся внутри объекта “Calculator” (Калькулятор).
— Интересно! Про ООП я понял, а почему в ФП все функции ни к чему не привязаны, находятся сами по себе?
Помните я говорил, что ФП основано на процессах (функциях)? Давайте опять попробуем спроецировать это на нашу жизнь. Например, возьмем процесс (функцию) создания вселенной, галактики, планеты, человека, процесс (функцию) сложения, вычитания и т.д. Сам процесс (функция) никому не принадлежит — он общий, пользоваться может кто угодно, но при этом каждый процесс (функция) имеет определенные условия, в зависимости от которых получается тот или иной результат. Рассмотрим простой пример из жизни, процесс “Приготовление ужина”: данный процесс может осуществить кто угодно, даже ваш кот или собака, но так как опыта у них нет, то, вероятнее всего, ужин просто не будет приготовлен. То есть получается, что воспользоваться могут все, но результат будет разный.
— Фух, ну теперь понятно, что такое парадигма программирования. Ну все-таки остался вопрос: почему где-то используется одна парадигма, а где-то другая?
Вы удивитесь, но на самом деле, две эти парадигмы можно использовать одновременно. Опять-таки, давайте рассмотрим простой пример, спроецированный на реальную жизнь. Представим, что у нас есть:
Заключение
Итак, в этой статье мы можно сказать на пальцах, без специфических терминов, разобрали основные различия парадигм ООП и ФП. К сожалению, тут нам пора остановиться. Теперь ваш ход – самостоятельно углублять знания с помощью дополнительной литературы.
Должен сказать, что я немного творчески подошел к написанию данной статьи: упростил терминологию, а где-то и вовсе заменил понятия метафорами, но все это только для того, чтобы любой человек, даже не имеющий никаких знаний в этой области, мог погрузиться в тему парадигм программирования. Я надеюсь, что данная статья была полезна будущем программистам и после ее прочтения удивительный мир программирования стал чуть ближе для каждого.
Парадигмы программирования: определение, виды и их особенности
Парадигмы программирования
Если представить условную иерархию, тогда парадигмы программирования будут на самой вершине. Именно они будут определят ь п равила написания, свойства, шаблоны, паттерны, структуру, архитектуру программы и др. То ест ь и значально идет парадигма, которая влечет все остальные собственные составляющие.
Основные парадигмы программирования
Парадигма объектно-ориентированного программирования
Объектно-ориентированному программированию свойственны такие принципы, как:
Парадигма функционального программирования
Парадигма функционального программирования заняла почетное второе место по популярности после объектно-ориентированного программирования. Главная идея такой модели заключается в том, что программа создается под одну конкретную задачу, поэтому от нее ждут конкретный результат.
В ООП основной компонент программы — объект. В функциональном программировании — функция. В функциях прописывается, какую информацию она должна получить в качестве входящего значения, а какую должна отдать. Функции могут быть вложенными — это когда одна функция является аргументом другой функции.
Программы, написанные по такой модели, обычно легче тестировать и читать, если сравнивать с ООП.
Процедурная парадигма программирования
Процедурная парадигма программирования основывается на процедурах. Процедура — это инструкция, которая используется для воздействи я на состояние памяти. После исполнения всего набора инструкци й п рограмма выдает результат.
Парадигма декларативного программирования
В такой парадигме программист описывает проблему и ожидаемый результат, но не дает инструкций программе, как достичь этого результата. В таком подходе отсутствуют переменные, состояния программы и операторы присваивания.
Логическая парадигма программирования
Такой вид программирования используется языками Prolog и Planner.
Заключение
Парадигмы программирования: что это такое и в чём различия
Когда программист начинает изучать свой первый язык — знакомится с его синтаксисом, пробует запускать простые команды, — он, как правило, не задумывается о том, в какой парадигме программирования работает. Только позже приходит осознание, что даже в пределах одного языка можно создавать разные в структурном плане программы. Выбрать ту парадигму, которая нравится, удаётся не всегда: во многом это зависит от актуальной ситуации в программировании, его тенденций, и, конечно, от языка.
Что такое парадигма программирования? Если в двух словах, то это набор принципов и методик по созданию кода. Они нужны, чтобы упорядочить программу и сделать её структурированной, удобной и понятной другим программистам, работающим в той же парадигме. Важно, чтобы и для вычислительной машины программа была ясной, подготовленной к быстрому и точному исполнению. Разберём основные парадигмы программирования.
Объектно-ориентированное программирование
Наиболее распространённая на данный момент парадигма. Это подвид императивного программирования — оно основано на последовательных вызовах команд, изменяющих данные, с которыми работает программа. Таким образом она оперирует объектами, и это удобно для многих приложений.
Новичков зачастую пугает аббревиатура ООП, но освоить парадигму объектно-ориентированного программирования не так сложно, как кажется. В своё время эта идея оказалась вирусной: создавать объекты, принадлежащие классам, а также использовать методы в качестве действий, которые может выполнить объект или которые можно произвести над ним. Много специалистов по Computer Science придерживаются такого подхода. Большое преимущество здесь в том, что программисту, использующему ООП, легко разобраться, что происходит в программе. Достаточно посмотреть, какие действия производит каждый из объектов.
Легче всего использовать ООП в Python, посложнее — в C++. Но если в этих языках у программиста ещё есть возможность увильнуть от ООП (например, для Python вполне подходит функциональное программирование), то в Java и C# всегда необходимо создавать классы, одних функций недостаточно.
Функциональное программирование
По распространённости функциональная парадигма программирования занимает второе место после ООП. Это развитие идей декларативного программирования: программа создаётся как инструмент, который решает определённую задачу и в итоге даёт нужный результат.
Между последователями разных парадигм, оказавшимися в одной ветке комментариев, всегда разгорается бесконечный холивар с обвинениями в «ООП/ФП головного мозга». Но бывают и программисты, готовые применить любой из подходов в зависимости от проекта.
Наиболее характерный для функционального программирования язык — Haskell. В реальных проектах он применяется редко, но будто создан для красивых решений в духе ФП — поэтому Haskell стал культовым среди профессиональных программистов, предпочитающих эту парадигму.
В ФП код программы состоит из функций, для которых подробно прописано, что должно быть на входе, а что — на выходе. Причём одну функцию вполне можно подать на вход другой в качестве аргумента. Так программа выполняется, запуская нужные функции.
Преимущества функционального подхода — в том, что код легко читать, а тестирование упрощается. Всё потому, что действия, производимые функцией, не зависят от внешнего состояния. Выполнение кода становится более предсказуемым, а неожиданные побочные эффекты — менее вероятными.
Помимо объектно-ориентированного и функционального программирования, есть и другие парадигмы. Некоторые из них уже вытеснены более современными подходами. Но есть и специфические парадигмы, которые нужны для конкретных ситуаций. Расскажем про несколько самых распространённых.
Процедурное программирование
Этот подход — разновидность императивной парадигмы программирования. Процедурами здесь называют команды, которые применяются в определённом порядке и последовательно меняют состояние памяти. После применения всех команд программа выдаёт результат.
Процедурное программирование применяется не только в классических языках вроде C и Pascal, но и в самых современных. Например, в Go, где помимо процедурного подхода можно применить и ООП, но с ограниченной функциональностью.
При процедурном подходе проще писать и поддерживать код, чем при объектно-ориентированном, но в то же время программы тяжелее масштабировать и создавать на их основе сложные проекты.
Метапрограммирование
Здесь программа, которую вы создаёте, сама генерирует код новой программы или модифицирует свой. С помощью этого подхода часть задач разработчика можно автоматизировать. А ещё он даёт возможность людям, не владеющим языками программирования, создавать программы с помощью графических интерфейсов или словесных команд на естественном языке — программа преобразует их в обычный код.
Обобщённое программирование
В этой парадигме программист создаёт обобщённые представления для классов и функций. То есть не просто классы, которые могут наследоваться (как в ООП), а шаблоны функций или классов (если применить такой подход в C++). Изначально у них отсутствуют требования типа данных для входных параметров, поэтому шаблоны можно сделать более универсальными.
Преимущество этой парадигмы в том, что можно создавать алгоритмы, которые будут работать с разными типами, и для этого не придётся добавлять реализации для каждого типа отдельно. Такой подход можно совместить как с ООП, так и с другими современными парадигмами программирования.
Логическое программирование
Логическое программирование — это подвид декларативного. Основан на выводе информации из заданных фактов и логических правил, которые к ним можно применить. При выполнении программ используются правила формальной логики.
Возможность применить эту парадигму заложена в языке Prolog — он позволяет вводить предложения в виде фактов и набора правил. Разработку Prolog начали ещё в 1970 году, и целью было понять естественный язык. Логика используется как средство формализовать его семантику. Если располагать фактической информацией о предметной области, можно автоматизировать выдачу информации по схеме «вопрос — ответ».
Хотя подавляющее большинство разработчиков используют объектно-ориентированное или функциональное программирование, эти парадигмы не стали абсолютными монополистами. Не исключено, что с развитием технологий — например, при переносе части вычислительных задач на квантовые компьютеры — актуальной станет какая-то новая, ещё не созданная парадигма.
Если вы хотите ближе познакомиться с разными языками, приглашаем вас на бесплатный вебинар «Основы программирования». На нём вы погрузитесь в основы профессии и определитесь, по какому пути хотите развиваться в мире разработки.
Когда программист начинает изучать свой первый язык — знакомится с его синтаксисом, пробует запускать простые команды, — он, как правило, не задумывается о том, в какой парадигме программирования работает. Только позже приходит осознание, что даже в пределах одного языка можно создавать разные в структурном плане программы. Выбрать ту парадигму, которая нравится, удаётся не всегда: во многом это зависит от актуальной ситуации в программировании, его тенденций, и, конечно, от языка.
Что такое парадигма программирования? Если в двух словах, то это набор принципов и методик по созданию кода. Они нужны, чтобы упорядочить программу и сделать её структурированной, удобной и понятной другим программистам, работающим в той же парадигме. Важно, чтобы и для вычислительной машины программа была ясной, подготовленной к быстрому и точному исполнению. Разберём основные парадигмы программирования.
Объектно-ориентированное программирование
Наиболее распространённая на данный момент парадигма. Это подвид императивного программирования — оно основано на последовательных вызовах команд, изменяющих данные, с которыми работает программа. Таким образом она оперирует объектами, и это удобно для многих приложений.
Новичков зачастую пугает аббревиатура ООП, но освоить парадигму объектно-ориентированного программирования не так сложно, как кажется. В своё время эта идея оказалась вирусной: создавать объекты, принадлежащие классам, а также использовать методы в качестве действий, которые может выполнить объект или которые можно произвести над ним. Много специалистов по Computer Science придерживаются такого подхода. Большое преимущество здесь в том, что программисту, использующему ООП, легко разобраться, что происходит в программе. Достаточно посмотреть, какие действия производит каждый из объектов.
Легче всего использовать ООП в Python, посложнее — в C++. Но если в этих языках у программиста ещё есть возможность увильнуть от ООП (например, для Python вполне подходит функциональное программирование), то в Java и C# всегда необходимо создавать классы, одних функций недостаточно.
Функциональное программирование
По распространённости функциональная парадигма программирования занимает второе место после ООП. Это развитие идей декларативного программирования: программа создаётся как инструмент, который решает определённую задачу и в итоге даёт нужный результат.
Между последователями разных парадигм, оказавшимися в одной ветке комментариев, всегда разгорается бесконечный холивар с обвинениями в «ООП/ФП головного мозга». Но бывают и программисты, готовые применить любой из подходов в зависимости от проекта.
Наиболее характерный для функционального программирования язык — Haskell. В реальных проектах он применяется редко, но будто создан для красивых решений в духе ФП — поэтому Haskell стал культовым среди профессиональных программистов, предпочитающих эту парадигму.
В ФП код программы состоит из функций, для которых подробно прописано, что должно быть на входе, а что — на выходе. Причём одну функцию вполне можно подать на вход другой в качестве аргумента. Так программа выполняется, запуская нужные функции.
Преимущества функционального подхода — в том, что код легко читать, а тестирование упрощается. Всё потому, что действия, производимые функцией, не зависят от внешнего состояния. Выполнение кода становится более предсказуемым, а неожиданные побочные эффекты — менее вероятными.
Помимо объектно-ориентированного и функционального программирования, есть и другие парадигмы. Некоторые из них уже вытеснены более современными подходами. Но есть и специфические парадигмы, которые нужны для конкретных ситуаций. Расскажем про несколько самых распространённых.
Процедурное программирование
Этот подход — разновидность императивной парадигмы программирования. Процедурами здесь называют команды, которые применяются в определённом порядке и последовательно меняют состояние памяти. После применения всех команд программа выдаёт результат.
Процедурное программирование применяется не только в классических языках вроде C и Pascal, но и в самых современных. Например, в Go, где помимо процедурного подхода можно применить и ООП, но с ограниченной функциональностью.
При процедурном подходе проще писать и поддерживать код, чем при объектно-ориентированном, но в то же время программы тяжелее масштабировать и создавать на их основе сложные проекты.
Метапрограммирование
Здесь программа, которую вы создаёте, сама генерирует код новой программы или модифицирует свой. С помощью этого подхода часть задач разработчика можно автоматизировать. А ещё он даёт возможность людям, не владеющим языками программирования, создавать программы с помощью графических интерфейсов или словесных команд на естественном языке — программа преобразует их в обычный код.
Обобщённое программирование
В этой парадигме программист создаёт обобщённые представления для классов и функций. То есть не просто классы, которые могут наследоваться (как в ООП), а шаблоны функций или классов (если применить такой подход в C++). Изначально у них отсутствуют требования типа данных для входных параметров, поэтому шаблоны можно сделать более универсальными.
Преимущество этой парадигмы в том, что можно создавать алгоритмы, которые будут работать с разными типами, и для этого не придётся добавлять реализации для каждого типа отдельно. Такой подход можно совместить как с ООП, так и с другими современными парадигмами программирования.
Логическое программирование
Логическое программирование — это подвид декларативного. Основан на выводе информации из заданных фактов и логических правил, которые к ним можно применить. При выполнении программ используются правила формальной логики.
Возможность применить эту парадигму заложена в языке Prolog — он позволяет вводить предложения в виде фактов и набора правил. Разработку Prolog начали ещё в 1970 году, и целью было понять естественный язык. Логика используется как средство формализовать его семантику. Если располагать фактической информацией о предметной области, можно автоматизировать выдачу информации по схеме «вопрос — ответ».
Хотя подавляющее большинство разработчиков используют объектно-ориентированное или функциональное программирование, эти парадигмы не стали абсолютными монополистами. Не исключено, что с развитием технологий — например, при переносе части вычислительных задач на квантовые компьютеры — актуальной станет какая-то новая, ещё не созданная парадигма.
Если вы хотите ближе познакомиться с разными языками, приглашаем вас на бесплатный вебинар «Основы программирования». На нём вы погрузитесь в основы профессии и определитесь, по какому пути хотите развиваться в мире разработки.
Чистая архитектура. Часть II — Парадигмы программирования
Эта серия статей – вольный и очень краткий пересказ книги Роберта Мартина (Дяди Боба) «Чистая Архитектура», выпущенной в 2018 году. Начало тут.
Парадигмы программирования
Дисциплину, которая в дальнейшем стала называться программированием, зародил Алан Тьюринг в 1938 году. В 1945 он уже писал полноценные программы, которые работали на реальном железе.
Первый компилятор был придуман в 1951 году Грейс Хоппер (бабушка с татуировкой Кобола). Потом начали создаваться языки программирования.
Обзор парадигм
Существует три основных парадигмы: структурное, объектно-ориентированное и функциональное. Интересно, что сначала было открыто функциональное, потом объектно-ориентированное, и только потом структурное программирование, но применяться повсеместно на практике они стали в обратном порядке.
Структурное программирование было открыто Дейкстрой в 1968 году. Он понял, что goto – это зло, и программы должны строиться из трёх базовых структур: последовательности, ветвления и цикла.
Объектно-ориентированное программирование было открыто в 1966 году.
Функциональное программирование открыто в 1936 году, когда Чёрч придумал лямбда-исчисление. Первый функциональный язык LISP был создан в 1958 году Джоном МакКарти.
Каждая из этих парадигм убирает возможности у программиста, а не добавляет. Они говорят нам скорее, что нам не нужно делать, чем то, что нам нужно делать.
Все эти парадигмы очень связаны с архитектурой. Полиморфизм в ООП нужен, чтобы наладить связь через границы модулей. Функциональное программирование диктует нам, где хранить данные и как к ним доступаться. Структурное программирование помогает в реализации алгоритмов внутри модулей.
Структурное программирование
Дейкстра понял, что программирование – это сложно. Большие программы имеют слишком большую сложность, которую человеческий мозг не способен контролировать.
Чтобы решить эту проблему, Дейсктра решил сделать написание программ подобно математическим доказательствам, которые также организованы в иерархии. Он понял, что если в программах использовать только if, do, while, то тогда такие программы можно легко рекурсивно разделять на более мелкие единицы, которые в свою очередь уже легко доказуемы.
С тех пор оператора goto не стало практически ни в одном языке программирования.
Таким образом, структурное программирование позволяет делать функциональную декомпозицию.
Однако на практике мало кто реально применял аналогию с теоремами для доказательства корректности программ, потому что это слишком накладно. В реальном программировании стал популярным более «лёгкий» вариант: тесты. Тесты не могут доказать корректности программ, но могут доказать их некорректность. Однако на практике, если использовать достаточно большое количество тестов, этого может быть вполне достаточно.
Объектно-ориентированное программирование
ООП – это парадигма, которая характеризуется наличием инкапсуляции, наследования и полиморфизма.
Инкапсуляция позволяет открыть только ту часть функций и данных, которая нужна для внешних пользователей, а остальное спрятать внутри класса.
Однако в современных языках инкапсуляция наоборот слабее, чем была даже в C. В Java, например, вообще нельзя разделить объявление класса и его определение. Поэтому сказать, что современные объектно-ориентированные языки предоставляют инкапсуляцию можно с очень большой натяжкой.
Наследование позволяет делать производные структуры на основе базовых, тем самым давая возможность осуществлять повторное использование этих структур. Наследование было реально сделать в языках до ООП, но в объектно-ориентированных языках оно стало значительно удобнее.
Наконец, полиморфизм позволяет программировать на основе интерфейсов, у которых могут быть множество реализаций. Полиморфизм осуществляется в ОО-языках путём использования виртуальных методов, что является очень удобным и безопасным.
Полиморфизм – это ключевое свойство ООП для построения грамотной архитектуры. Он позволяет сделать модуль независимым от конкретной реализации (реализаций) интерфейса. Этот принцип называется инверсией зависимостей, на котором основаны все плагинные системы.
Инверсия зависимостей так называется, что она позволяет изменить направление зависимостей. Сначала мы начинаем писать в простом стиле, когда высокоуровневые функции зависят от низкоуровневых. Однако, когда программа начинает становиться слишком сложной, мы инвертируем эти зависимости в противоположную сторону: высокоуровневые функции теперь зависят не от конкретных реализаций, а от интерфейсов, а реализации теперь лежат в своих модулях.
Любая зависимость всегда может быть инвертирована. В этом и есть мощь ООП.
Таким образом, между различными компонентами становится меньше точек соприкосновения, и их легче разрабатывать. Мы даже можем не перекомпилировать базовые модули, потому что мы меняем только свой компонент.
Функциональное программирование
В основе функционального программирования лежит запрет на изменение переменных. Если переменная однажды проинициализирована, её значение так и остаётся неизменным.
Какой профит это имеет для архитектуры? Неизменяемые данные исключают гонки, дедлоки и прочие проблемы конкурентных программ. Однако это может потребовать больших ресурсов процессора и памяти.
Применяя функциональный подход, мы разделяем компоненты на изменяемые и неизменяемые. Причём как можно больше функциональности нужно положить именно в неизменяемые компоненты и как можно меньше в изменяемые. В изменяемых же компонентах приходится работать с изменяемыми данными, которые можно защитить с помощью транзакционной памяти.
Интересным подходом для уменьшения изменяемых данных является Event Sourcing. В нём мы храним не сами данные, а историю событий, которые привели к изменениям этих данных. Так как в лог событий можно только дописывать, это означает, что все старые события уже нельзя изменить. Чтобы получить текущее состояние данных, нужно просто воспроизвести весь лог. Для оптимизации можно использовать снапшоты, которые делаются, допустим, раз в день.
Заключение
Таким образом, каждая из трёх парадигм ограничивает нас в чём-то:
Структурное отнимает у нас возможность вставить goto где угодно.
ООП не позволяет нам доступаться до скрытых членов классов и навязывает нам инверсию зависимостей.