Императивное, декларативное, процедурное, объектно-ориентированное (ООП) и функциональное

Знание этих стилей парадигмы поможет вам более эффективно читать и писать код. Вот введение с примерами и терминологией.

Если вас интересует какая-то конкретная парадигма, например объектно-ориентированное программирование (ООП), не стесняйтесь переходить к этому разделу для ознакомления.

Содержание

  1. Введение: Что такое «парадигма программирования»?
  2. Императивное и декларативное программирование
  3. Процедурное программирование
  4. Объектно-ориентированное программирование
  5. Функциональное программирование
  6. Заключительные замечания

1. Введение. Что такое «парадигма программирования»?

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

Парадигмы программирования часто используются для:

  1. Описывать способы организации кода
  2. Помогите классифицировать языки программирования на основе их особенностей

Различные парадигмы развивались с течением времени на основе коллективных идей и уроков, извлеченных из прошлых программистов. Вот почему парадигмы обычно ограничивают код полезным образом.

Важно отметить, что эти парадигмы можно использовать строго по отдельности, но их часто комбинируют в современных высокоуровневых мультипарадигмальных языках, таких как JavaScript и Python. Даже если вы не придерживаетесь строго какой-то одной парадигмы, стратегии, которым вы научитесь из разных парадигм, сделают вас лучшим программистом в целом.

2. Императивное и декларативное программирование

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

Императивное программирование использует операторы, которые описывают, как изменять состояние программы шаг за шагом. Вот пример императивного программирования:

 1| var name = "Lea"
 2| var greeting = "Hi!";
 3| var message = name + " says " + greeting;
 4| // Result: The message is "Lea says Hi!"
  • Этот код JavaScript показывает серию операторов, которые изменяют состояние программы.
  • Поток управления часто достигается с помощью условного ветвления (например, if, else или switch) и циклов (например, for или while).
  • Императивное программирование является старейшим и наиболее близким к машинному языку, поэтому многие низкоуровневые языки программирования, такие как ассемблер, считаются императивными языками программирования.

Декларативное программирование описывает какое назначение программы, а не описывает, как это сделать. Вот пример декларативного программирования:

 1| SELECT name, greeting
 2| FROM people_table
 3| WHERE name='Lea'
 4| -- Result: Lea | Hi!
  • Этот код SQL описывает то, что мы хотим, а не то, как это получить
  • Декларативное программирование иногда используется как общий термин для описания кода, который не является императивным.
  • Другие примеры декларативных языков включают HTML, CSS и регулярные выражения.

3. Процедурное программирование

В процедурном программировании многократно используемые группы кода, называемые процедурами или функциями, используются для изменения состояния программы. Это считается типом императивного программирования.

Вот пример процедурного программирования:

 1| var message = "";
 2| function updateMessage(person, greeting) {
 3|     message = person + " says " + greeting;
 4| }
 5| 
 6| updateMessage("Lea", "Hi!");
 7| // Result: The message is "Lea says Hi!"

Сначала мы определяем переменную с именем message и процедуру ее изменения с именем updateMessage (определяется с помощью ключевого слова function, поскольку это JavaScript). Процедура updateMessage вызывается (активируется) в строке 6; он принимает два текстовых аргумента («Леа» и «Привет!»), назначает их своим переменным параметрам (person и greeting) в зависимости от порядка, а затем изменяет значение message на «Леа говорит привет! ». Мы могли бы снова вызвать эту процедуру с другими аргументами, чтобы изменить сообщение во второй раз, например так:

 8| updateMessage("Lukas", "Hey");
 9| // Result: Now the message changed to "Lukas says Hey"

Процедуры и функции также могут возвращать вычисленное значение, например:

 9| function getMessage(person, greeting) {
10|     return person + " says " greeting;
11| }
12| newMessage = getMessage("Emilie", "Bonjour!")
13| // Result: newMessage is "Emilie says Bonjour!"

Общая терминология процедурного программирования:

  • Процедура/подпрограмма: группа кодов, которые можно активировать дистанционно
  • Call: означает активировать процедуру
  • Параметры: переменные, которые определяют, какие входные данные могут быть переданы процедуре.
  • Аргументы: входные данные, предоставленные процедуре и назначенные ее параметрам.
  • return: общее ключевое слово для определения вывода процедуры или функции.

Ключевые выводы для процедурного программирования

Используйте процедурное программирование, чтобы сделать ваш код более:

  • Понятный (с использованием хорошо названных процедур для добавления логических разделений)
  • Многократное использование (используя одни и те же процедуры несколько раз и используя аргументы процедуры для их использования по-разному)

4. Объектно-ориентированное программирование (ООП)

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

Структуры данных Class и Object являются краеугольным камнем объектно-ориентированного программирования. Класс — это план, из которого вы можете создавать объекты. Например, если у вас есть план класса под названием «Человек», вы можете создать разных людей, таких как Леа и Лукас, из плана «Человек».

Вот пример объектно-ориентированного программирования (ООП) с использованием JavaScript. В этом примере наша цель — заставить человека по имени Леа сказать «Привет!». Начнем с определения класса (схемы) с именем Person. Класс Person будет содержать конструктор, два свойства и метод:

  • Человек конструктор (используется для создания объекта из класса): constructor
  • Свойства человека (атрибуты, хранящиеся как переменные): name и greeting
  • Человек метод (поведение хранится как функция): greet()
 1| class Person {
 2|    constructor(name, greeting) {
 3|        this.name = name;
 4|        this.greeting = greeting;
 5|    }
 6|    greet() {
 7|        console.log(this.name + " says " + this.greeting);
 8|    }
 9| }

Далее мы можем создать объект «Lea» из нашего класса Person:

10| var leaObject = new Person("Lea", "Hi!");

Строка 10 создает объект Lea, используя класс Person в качестве схемы. Из-за того, как написан метод constructor класса, первый аргумент («Lea») назначается свойству name, а второй аргумент («Привет!») назначается свойству greeting.

11| leaObject.greet();
12| // Result: "Lea says Hi!"

Строка 11 вызывает функцию greet() leaObject, чтобы Леа сказала «Привет!». Обратите внимание на нотацию «точка»: точка (.) — это общий синтаксис, используемый для доступа к свойству или методу внутри предыдущего объекта. В данном случае мы вызываем метод greet, функцию, являющуюся частью leaObject.

Мы также можем использовать класс Person, чтобы легко создавать больше людей, каждый из которых имеет свой собственный встроенный метод greet():

13| var lukasObject = new Person("Lukas", "Hallo!");
14| var emilieObject = new Person("Emilie", "Bonjour!");
15| 
16| lukasObject.greet();  // Result: "Lukas says Hallo!"
17| emilieObject.greet(); // Result: "Emilie says Bonjour!"

Другой распространенной, но более сложной функцией ООП является наследование. Вы также можете использовать наследование для создания класса на основе другого класса. Ниже мы создаем более конкретный тип Person, называемый Player, который имеет все те же свойства и методы, что и Person, но также некоторые дополнительные:

18| class Player extends Person {
19|     constructor(name, greeting, score) {
20|         super(name, greeting);
21|         this.score = score;
22|     }
23|     sayScore() {
24|         console.log(this.name + "'s score is " + this.score)
25|     }
26| }
27| 
28| const tobyObject = new Player("Toby", "Hello", 90);
29| 
30| tobyObject.greet(); // Result: "Toby says Hello"
31| tobyObject.sayScore(); // Result: "Toby's score is 90"

Поскольку класс Player расширяет класс Person, новый tobyObject имеет свойства и методы как Person, так и Player. Оператор super в строке 20 предоставляет их из класса Person. Наследование упрощает написание новых классов и помогает организовать взаимосвязь между ними интуитивно понятным способом.

Общая терминология ООП:

  • Класс: схема создания логических групп кода
  • Объект: объект, созданный из плана класса.
  • Свойство: переменная, являющаяся частью класса или объекта, которая описывает аспект или состояние объекта.
  • Метод: функция, которая является частью класса или объекта и позволяет ему действовать
  • constructor: общий термин и ключевое слово для специального метода, используемого только для создания объектов из класса.
  • this или self: общие ключевые слова для обозначения других частей того же объекта изнутри.
  • Инкапсуляция: свойства и методы могут иметь такие обозначения, как общедоступные или частные, чтобы более тщательно контролировать использование класса.
  • Абстракция: лучше всего сделать классы простыми в использовании, предоставляя только простые общедоступные методы класса, которые скрывают более сложную функциональность.
  • Наследование: новый класс может быть создан на основе другого класса; новый производный класс наследует все свойства и методы базового класса
  • Полиморфизм: метод может менять функциональность в зависимости от ситуации; например, если у объекта Person есть метод getName(), они могут сказать «Lea», но если объект Player наследует и изменяет функциональность класса Person, их метод getName() может возвращать онлайн-имя, например «Lea_042».

Основные выводы по объектно-ориентированному программированию (ООП)

Используйте объектно-ориентированное программирование, чтобы сделать ваш код более:

  • Понятный (за счет группировки поведения в классы и объекты и обеспечения четких взаимосвязей между различными частями программы)
  • Многократное использование (используя классы для создания объектов и используя наследование и полиморфизм для повторного использования классов и их методов)
  • Стабильный (за счет инкапсуляции и защиты кода внутри объектов, а также за счет предоставления четких конечных точек для автоматизированного тестирования, улучшения рефакторинговой способности)

5. Функциональное программирование

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

Вот пример, демонстрирующий некоторые концепции функционального программирования:

 1| function makeIntro(name) {
 2|     return name + " says ";
 3| }
 4| function makeGreeting(makeIntroFunction, name, message) {
 5|     return makeIntroFunction(name) + message;
 6| }
 7| 
 8| console.log(makeGreeting(makeIntro, "Lea", "Hi!"));
 9| // Result: "Lea says Hi!"

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

В строке 8 происходит все действие. Поскольку все функции являются чистыми, меньше шансов, что внешнее изменение неожиданно изменит результат этого кода. Это одно из преимуществ функционального программирования. Код также достаточно модульный. Например, в настоящее время результатом является «Леа говорит привет!», но если вы хотите изменить стиль вступления, вы можете просто указать другую функцию makeIntro для makeGreeting и вместо этого получить что-то другое, например «[Леа]: Привет!».

Общая терминология функционального программирования:

  • Функция: процедура, которая выполняет задачу и возвращает результат
  • первоклассные функции: когда функции рассматриваются как любая другая переменная; их можно изменять, назначать переменным и даже передавать и возвращать через другие функции (известные как функции более высокого порядка).
  • Чистая функция: функция, которая 1) использует только данные, предоставленные через ее параметры, обеспечивая согласованность (без скрытых входных данных, таких как глобальные переменные), и 2) не имеет побочных эффектов. (нет скрытых выходов)
  • Побочные эффекты: когда функция изменяет или мутирует что-либо вне области действия функции, в обход инструкции return (например, скрытые выходные данные, такие как изменяемые данные и вызовы процедурных функций)

Основные выводы по функциональному программированию

Используйте функциональное программирование, чтобы сделать ваш код больше:

  • Стабильный (используя чистые функции для снижения вероятности ошибок во время выполнения, вызванных скрытыми входными данными и скрытыми выходными данными, предоставляя четкие конечные точки для автоматического тестирования, и улучшая рефакторинг благодаря обоим этим преимуществам)
  • Многократное использование (заставляя функции быть более модульными и позволяя передавать первоклассные функции в качестве аргументов для глубокой настройки)
  • Понятный (путем упрощения логики с помощью строгих чистых параметров функции и демонстрации намерений заранее)

6. Заключительные замечания

  • Мультипарадигмальные языки программирования, такие как JavaScript и Python, поддерживают использование всех пяти популярных парадигм программирования, и часто хорошие программные решения используют комбинацию этих стилей. Например, иногда использование чистых функций для методов класса ООП может сделать код более проверяемым и понятным.
  • Пять парадигм программирования, обсуждаемые в этой статье, в настоящее время являются наиболее используемыми и обсуждаемыми, но есть и другие, включая логическое программирование и структурированное программирование.
  • Попробуйте применить концепции из этих парадигм, чтобы написать более стабильный, понятный и пригодный для повторного использования код.
  • Удачного кодирования!

Дополнительная литература: