Я твердо убежден, что если вы разработчик, вам необходимо знать, как работают вещи за пределами вашей зоны комфорта, даже если вы не планируете выходить за ее пределы в ближайшее время. Тот факт, что вы знаете, что есть другой способ сделать что-то, сам по себе открывает вам горизонт для новых методологий и технологий.
Конечно, их нужно попробовать, иначе вы мало что сделаете, однако прыжок, необходимый для перехода от "чтения" к "пробованию", на самом деле не так уж велик, так что подумайте о том, чтобы попробовать хотя бы одну из 4 парадигм программирования, о которых я расскажу здесь. Вы наверняка узнаете кое-что новое, а если вы используете язык, совместимый с несколькими из них, то вам даже не придется далеко ходить, чтобы попробовать их.
Так давайте же испачкаем руки!
Процедурное программирование
Большинство разработчиков начинают с этого языка, даже если не знают его по имени. Однако, как только они начинают изучать парадигмы, они довольно часто путают его с Функциональным программированием, потому что, в конце концов, большинство современных языков программирования переименовали слово "процедура" в "функция". Это вызывает множество неловких моментов во время технических собеседований, когда разработчика спрашивают о FP, а он утверждает, что использует его уже много лет, но в то же время не может ответить на вопросы типа "что такое чистая функция?" или "что такое HOF?".
Процедурное программирование - это не что иное, как работа с процедурами (они же функции) и обычными структурами данных. Мы не говорим здесь о "функциональном программировании", пока нет, потому что эта парадигма не имеет дело с некоторыми концепциями (такими как функции высшего порядка), которые делают ПП, ну... ПП.
Так что же такого особенного в ПП?
(хе-хе... Я простой человек - и ребенок - я знаю, но я читаю "ПП" и смеюсь).
Его легко освоить, вот почему, на мой взгляд, большинство разработчиков (как самоучек, так и выходцев из таких мест, как университеты) склонны учиться сначала. Вам не нужно понимать множество сложных концепций, чтобы освоить его. Как только вы поняли, что такое основные типы, потоки управления и условные выражения, следующий логический шаг - понять, как сгруппировать эти строки кода в нечто, что может быть вызвано несколько раз из разных мест. Другими словами: процедура.
Многие языки позволяют вам работать таким образом, не навязывая вам определенную парадигму: JavaScript, Python, PHP - это всего лишь три примера.
Хорошая ли это парадигма для начала вашей карьеры? Чертовски да! У нее есть две основные характеристики, которые делают ее идеальным первым выбором: она проста и подходит для всего, что вы хотите сделать.
Недостатки процедурного программирования
Хорошо ли использовать эту парадигму в составе большой команды разработчиков, работающих над крупным продуктом? Возможно, не идеально. И послушайте меня, возможно, вы создаете с ее помощью отличное программное обеспечение, но вы либо используете концепции из других парадигм для расширения этой, либо не используете преимущества других, более сложных парадигм.
Основная проблема, которую я испытываю с PP, заключается в том, что она слишком упрощена:
Вы не можете логически сгруппировать состояние и поведение, если не считаете, что у вас есть модули, но даже в этом случае, если вы используете их для моделирования классов и объектов, то вы заимствуете концепции из других парадигм, вы не остаетесь в рамках PP.
Не имея доступа к некоторым концепциям ПП, вы вынуждены писать императивный код, который несколько сложнее для мысленного разбора и понимания, чем декларативный код. Другими словами, вместо того, чтобы говорить компилятору/парсеру, что вы хотите, чтобы произошло после выполнения вашего кода, вы должны сказать ему, как это должно произойти. Это разница между тем, чтобы сказать "для каждого элемента внутри этого списка элементов, пожалуйста, пройдите по одному, умножьте их на два и сохраните результат в конце этого нового списка, который я только что создал" (императивный) и тем, чтобы сказать "сохраните результат отображения каждого элемента этого списка в эту новую переменную, которую я только что создал" (декларативный).
Это приводит к классическим плохим практикам, таким как злоупотребление глобальными переменными, поскольку не предоставляет элегантных альтернатив. Конечно, вы можете передавать атрибуты от вызова функции к вызову функции, но если у вас много вложенных вызовов, это может закончиться тем, что вы будете иметь параметры из глобального контекста, сверлящие вызовы функций просто для того, чтобы они могли быть использованы на 3 уровня ниже.
Этот код ужасен, потому что нам приходится передавать константу myName через 3 различные функции, прежде чем я смогу добраться до той части логики, которой она действительно нужна. Это создает много беспорядка в коде и делает его трудночитаемым и трудно поддерживаемым.
Давайте рассмотрим некоторые альтернативы, которые расширяют концепции PP для создания лучшего опыта разработчика.
Совет: Создавайте что угодно из независимых компонентов
Попрощайтесь с монолитными приложениями и оставьте позади слезы их разработки.
Будущее - это компоненты; модульное программное обеспечение, которое быстрее, масштабируемее и проще в сборке. Инструменты OSS, такие как Bit, предоставляют разработчикам отличные возможности для создания независимых компонентов и компоновки приложений. Многие команды начинают с создания своих систем проектирования или микрофронтендов, используя общие компоненты.
Функциональное программирование
FP часто является очень неправильно понимаемой парадигмой просто из-за того, как она освещается, в основном, в формальном образовании. Многие разработчики, проходящие обучение в колледже, узнают о ФП из таких языков, как Haskell и Clojure. И примеры использования, которые они рассматривают, иногда очень ориентированы на математику.
Не поймите меня неправильно, это очень пуристский способ обучения, и если вам повезет, и вы продолжите изучать эту тему, то узнаете, что FP действительно можно использовать во многих реальных приложениях, и что он также значительно упрощает работу.
Однако это не так, многие разработчики не знают, что у них есть язык Scala, основанный на JVM, который они могут использовать, или что JavaScript поддерживает многие из основных концепций FP из коробки.
Что крутого в FP?
Одним из главных аргументов в пользу FP, особенно если вы спросите меня, является то, что вы можете легко писать очень декларативный код.
Вы можете очень близко подойти к тому, чтобы рассказать историю о том, что должна делать ваша бизнес-логика, не вдаваясь в неприятные детали реализации.
Любой из приведенных выше примеров оставляет очень мало возможностей для интерпретации или, скорее, вам не нужно понимать язык, чтобы понять логику. Первый пример имеет немного более математический подход, он очень похож на f(g(x)), где вам сначала нужно g(x), чтобы затем вызвать функцию f . Однако, учитывая имена в коде, вы можете быстро разобраться и с этой частью.
Кроме того, поскольку в FP мы имеем дело с концепцией "чистой функции" (функция, не имеющая побочных эффектов и возвращающая один и тот же результат каждый раз, когда она выполняется с одним и тем же входом), это упрощает такие задачи, как:
Юнит-тестирование. Гораздо проще тестировать то, что, как вы знаете, не может повлиять ни на что за пределами своей непосредственной области.
Это гораздо более безопасно, учитывая отсутствие непредвиденных побочных эффектов (т.е. вызывая функцию, вы не измените случайно глобальную переменную).
Сигнатуры чистых функций гораздо более значимы, чем сигнатуры обычных функций. Это связано в основном с тем, что обычные (или, скорее, нечистые) функции не обязаны следовать строгому контракту, они могут получать данные извне (даже из глобальной области видимости), и их вывод также не имеет ограничений. Учитывая то, как работают чистые функции, у них нет такой роскоши, поэтому сигнатура функции является ключевой для определения того, к каким данным они имеют доступ и что (если что) они собираются вернуть.
Есть ли недостатки у FP?
Основным недостатком я вижу то, как обычно преподается функциональное программирование. Студенты обычно не замечают, что это очень полезная методология из-за типа проблем, которые их учат решать с ее помощью.
На самом деле, очень немногие из используемых языков обычно встречаются вне образовательных контекстов (когда вы в последний раз видели программу на Haskell в свободном доступе?). Это означает, что даже если бы они захотели, им трудно даже экстраполировать знания самостоятельно, потому что у них даже нет подходящих инструментов для начала.
Если вместо этого им показать более практические примеры использования, например, включение и смешивание функционального программирования с другими парадигмами, как это делают Python и JavaScript, то они быстро поймут преимущества и полюбят его гораздо быстрее.
Заметьте, что я сказал "также показано", а не "вместо". Я не считаю, что Haskel, Clojure или любой другой язык, используемый для формального обучения FP, не должен использоваться. Однако "пуристский" подход, которого обычно придерживаются, - это тот подход, который мог бы выиграть от выбора более практического пути.
Логическое программирование
Вот уж поистине кривая дорожка, если вы когда-либо видели такую в нашей индустрии. Логическое программирование ломает плесень во всех аспектах, когда дело доходит до решения проблем.
Если вы пропустили объекты на FP, то с этим вы пропустите все, поверьте мне.
Логическое программирование - это еще одна парадигма, корни которой уходят в математику и - очевидно - логику. Вместо того, чтобы иметь дело с кодом, циклами и условными предложениями, оно имеет дело с фактами и правилами. Позвольте мне объяснить.
В мире LP, когда вы хотите решить проблему, это решение принимает форму ответа на вопрос. А чтобы вы могли задать вопрос, вам сначала нужно построить мир вокруг него (его контекст).
Поэтому вы начинаете с утверждения некоторых фактов, которые, по сути, являются истинными сведениями о сущностях.
Фантастика, вот наш факт. Никто не будет оспаривать этот факт.
Во-вторых, мы начинаем писать правила, которые являются умозаключениями о нашей области.
Ответ на наш вопрос, основанный на наших фактах и правилах, конечно же, будет "да". И хотя этот пример глуп и чрезмерно упрощен, он также показывает невероятную силу, которой обладает Логическое программирование. Потому что я не написал ни одной строчки кода, не того кода, который мы привыкли писать.
Мне не пришлось составлять список характеристик умных людей, и я нигде не написал оператора IF. Честно говоря, я просто сосредоточился непосредственно на поставленной задаче, а низкоуровневые вещи были выведены интерпретатором, круто, не правда ли?
Одним из основных языков LP является Prolog, и хотя вы, возможно, никогда не слышали о нем раньше, он используется в очень нишевых областях в некоторых отраслях промышленности. Если вы хотите узнать больше и увидеть больше интересных примеров использования языка LP для чего-то другого, кроме ответа на очень простой вопрос, загляните сюда:
Есть ли недостатки у Пролога?
Да, есть, но это не делает его бесполезным, это просто делает его неизвестным для большинства нашего сообщества разработчиков. Главный недостаток в том, что он настолько хорошо решает очень немногие и очень ограниченные проблемы, что большинство разработчиков (которые никогда не сталкивались с такими проблемами) не знают об этом.
Фактически, многие разработчики, занимающиеся, например, искусственным интеллектом, который является одной из основных отраслей, где он сейчас используется, используют другие классические подходы, потому что никогда не слышали о нем. Вот насколько нишевым является LP!
Другая проблема с LP заключается в том, что он настолько отличается от того, как мы привыкли писать код, что рассматривать его в качестве реального решения одной из наших проблем становится просто прыжком веры. Пока вы не попробуете и не поиграете с ним достаточно долго, вы не сможете понять, действительно ли это подходящий инструмент для работы. И даже если вам кажется, что вы видите потенциал LP, для того чтобы перейти к его использованию, потребуется много практики и головной боли.
Не поймите меня неправильно, если вы рассматриваете LP, сделайте это, дайте ему честный тест-драйв. Но убедитесь, что вы понимаете, что освоение Prolog или любой другой альтернативы LP не похоже на переход от PP к FP, расстояния больше, и переключение ментальной модели будет более значительным.
Объектно-ориентированное программирование
Наконец, ООП - это последняя парадигма, о которой я хотел рассказать. Большинство из вас, вероятно, знают о ней и используют ее годами, но я уверен, что другие не знают, и поэтому я решил рассказать о ней здесь.
Возможно, вы только начинаете и используете язык, который не поддерживает ООП, но позволяет вам окунуться в эти кишащие объектами воды. Это отличный способ начать, потому что вы можете медленно начать внедрять все больше и больше в процедурную среду.
Так в чем же дело с ООП? Ну, в мире ООП вы используете очень материальный подход к представлению вашей проблемной вселенной с помощью таких понятий, как классы и объекты. Другими словами, вы пытаетесь дать характеристики сущностям, с которыми хотите работать, как если бы они были физическими объектами, сидящими рядом с вами. Например, если вы хотите представить концепцию двери в видеоигре, вы можете создать класс, представляющий тип объекта, такой как GameObject, который имеет такие характеристики, как координаты, где находится этот объект, сцена игры, где он должен появиться, может быть, даже числовое значение, представляющее количество очков попадания, которые имеет дверь. Суть в том, что вы классифицировали объект как GameObject и через эту классификацию придали ему некоторые свойства, которые могут разделять другие объекты в этом игровом мире (другие GameObject'ы, если хотите).
Эта общая классификация, которая позволяет вам группировать объекты вместе, называется "класс", а каждый объект, который соответствует этому классу, называется "объект".
Следует помнить, что хотя эти понятия прекрасно подходят для представления объектов реального мира в нашем коде, они могут - и обычно используются - для представления любого типа сущностей. Например, связанный список значений можно представить с помощью нескольких классов, и в этом нет ничего осязаемого. Но тот факт, что вы мысленно превращаете эти абстрактные понятия в физические объекты, делает ООП таким интуитивно понятным, по крайней мере, в отношении основных понятий парадигмы.
В чем преимущество ООП?
Почему ООП лучше, чем ПП? Это неправильный вопрос, это не лучше и не хуже! Это разные вещи.
Некоторые люди будут гораздо больше идентифицировать себя с ООП, чем другие, которые могут предпочесть FP для того типа проблем, которые им нужно решить. Или любой другой парадигме.
Тем не менее, в этом подходе есть некоторые интересные преимущества:
Тем не менее, у этого подхода есть несколько интересных преимуществ:
Учитывая то, что он имеет дело с превращением абстрактных концепций в физические объекты, вы можете легко представить эти объекты и их взаимосвязи с помощью графических диаграмм, таких как UML. Это позволяет вам представить высокоуровневый обзор сложной бизнес-логики на графической диаграмме. Это, в свою очередь, упрощает задачу планирования и демонстрации идей и концепций другим до того, как вы напишете хоть одну строчку кода.
Это интуитивно понятно. Хотя не каждая проблема может быть решена с помощью ООП, те, которые решаются, легче рассуждать с помощью этой ментальной модели. Мы созданы для работы с ними и манипулирования ими, поэтому их использование для обдумывания наших проблем становится второй натурой.
Логика там, где должна быть логика. Подумайте об этом так: кроме свойств двери или даже объекта GameObject, вы также захотите взаимодействовать с этими объектами. Что логичнее? Поместить этот код в отдельный файл, наполненный несвязанными функциями, или поместить все в один файл, все в пределах сущности под названием "класс" и убедиться, что каждый его экземпляр (и ничто другое) имеет доступ к этому коду?
Существует масса заранее установленных шаблонов, которые помогут вам решить ваши проблемы. Отчасти это связано с парадигмой, а отчасти с тем, что она настолько широко используется, что многие разработчики придумали шаблоны проектирования, чтобы помочь шаблонизировать некоторые типичные решения. Например, если вам нужно получить доступ к объекту, который может быть создан только один раз, вы можете прибегнуть к шаблону Singleton, если вам нужна реактивность в вашей логике, вы можете воспользоваться шаблоном Observer и так далее. Это не готовые решения, а скорее шаблоны, которые вы можете включить в свой код, чтобы помочь вам найти лучший способ решения ваших проблем.
Список можно продолжать и продолжать, ведь это одна из основных парадигм программирования в настоящее время, она была проанализирована, исследована и расширена до предела.
Есть ли обратная сторона ООП?
Конечно, есть! Не существует серебряной пули, когда дело касается парадигм программирования, и ООП не является исключением.
Некоторые проблемы не поддаются рассуждениям в терминах физических (или даже концептуальных) объектов. Это означает, что, используя подход ООП, вы пытаетесь втиснуть квадрат в круг. Если круг достаточно велик или квадрат достаточно мал, он впишется, и вы заставите его работать, но это никогда не будет идеальным совпадением.
Другой недостаток ООП в том, что он очень сложен. Мое объяснение здесь только царапает поверхность всего, что представляет собой ООП, я даже не обсуждал наследование, инкапсуляцию, полиморфизм или любые другие причудливые слова, которые звучат вокруг этой парадигмы.
Для полного понимания и использования преимуществ ООП требуются годы чтения и опыта вместе взятые. Конечно, от этого оно не становится менее мощным, но его определенно нелегко освоить.
Наконец, будучи такой мощной парадигмой, некоторые языки доводят ее до крайности, создавая много накладных расходов просто для того, чтобы соответствовать каждой части парадигмы. Позвольте мне объяснить: некоторые языки являются 100% ООП, что означает, что они не принимают никакой другой альтернативы, и если вы используете их для решения простых задач, вам придется написать больше кода, чем на самом деле необходимо для решения поставленной задачи. Это, конечно, оправдано, если вы работаете над большим приложением, требующим большой внутренней структуры. Так что это скорее вопрос использования их для решения правильного типа проблем.
Я всегда говорю, что языки программирования - это как инструменты, нужно использовать подходящий для каждого вида деятельности, и парадигмы программирования точно такие же. Они не предназначены для решения каждой проблемы, некоторые из них лучше работают для более широкого круга задач, в то время как другие обладают меньшей гибкостью. В любом случае вы сможете извлечь из них максимум пользы, если поймете, какие проблемы они помогают решать и какие языки могут помочь в работе над тем или иным.
Как разработчику, знание о различных парадигмах поможет вам обдумать способ решения каждой проблемы. А если вам повезло, и вы используете язык, который позволяет использовать несколько парадигм, вы можете адаптировать свой стиль кодирования, не меняя языка. Это отличный способ практиковать новую парадигму с минимальными последствиями.