62 подписчика
5 советов по проектированию функций, которые значительно улучшат ваш код (твиттер-тред)
Обычно, от функций ожидают сокращения дублирования кода. Да, функции устраняют дублирование, но лишь в дополнение к тому, зачем они нужны. Настоящий смысл функции – повышение уровня абстракции. Звучит немного абстрактно, поэтому раскроем подробнее
Мир сложная штука, но эта сложность спрятана за простыми и понятными вещами. Например, работать с клавиатурой может даже ребенок, но лишь потому что она хорошо спроектирована и прячет от нас детали реализации. Нам не нужно знать физических законов для ее использования
Клавиатура, в свою очередь, состоит из деталей, которые тоже имеют достаточно высокий уровень абстракции: микросхема, мембраны, корпус, индикаторы. Все это на поверхности и достаточно далеко от реально протекающих процессов внутри
То о чем мы говорим, называется слоями абстракции. Любую систему можно разбить на набор слоев, в которых каждый следующий слой, строится на базе предыдущего слоя, что создает ощущение простоты. Мы не думаем о системе до самой глубины, нам достаточно знать про текущий слой
Из этого вытекает логичное, но, почему-то, редко обсуждаемое правило. На самом верхнем уровне, проектирование кода должно идти от слоев, которые 1) имеют четко выраженную ответственность 2) оперируют только абстракциями более низкого уровня 3) не прыгают через уровни
В самом общем виде эта мысль описана в SOC https://en.wikipedia.org/wiki/Separation_of_concerns… Примерами здесь служат: модель OSI, HTML/CSS, Миксины и многое другое. Это очень близко нашей теме, хотя и не один в один. Плюс хорошая адаптация к коду от Мартина: Принцип Single Level Of Abstraction
При этом тот же Мартин рекомендует в своей книге "делайте функции настолько мелкими настолько это возможно". Это довольно вредный совет, потому что начинается код ради кода и, в итоге, все усложняется. Функции не должны быть маленькими, функции должны соответствовать абстракции
Возникает вопрос, а на что конкретно ориентироваться чтобы создавать функции хорошо? И у меня есть ответ. Существует довольно много простых и не очень принципов, которые определяют как лучше проектировать функции. Большая часть из них универсальна и подходит для всех языков
Начнем с самого главного правила в программировании: отделение чистого кода от кода с побочными эффектами или "функциональное ядро и императивная оболочка". Чтобы понять о чем тут идет речь, поговорим о том, что такое чистые функции.
Чистая функция – детерминированная функция, без побочных эффектов. Детерминированность означает возврат одного и того же результата на один и тот же вход в рамках одного запуска программы. При следующем запуске результат может быть другим, но постоянным до остановки.
Побочные эффекты – влияние функции на внешнее окружение: модификация глобальных переменных, аргументов переданных по ссылке, ввод/вывод (чтение и запись файлов, сеть, консоль и т.п), вызов внутри себя других функций с побочными эффектами
Чистые функции имеют непосредственное отношение к понятию "бизнес-логика", алгоритмической части программы, которая занимается сутью бизнеса, ради которого она написана. Например много логики в автопилотах, бухгалтерском софте, логистическом, банковском и так далее.
Эта логика, сама по себе, не имеет отношения к коду. Она, часто, существует на уровне документов и спецификаций, ее понимают заказчики бизнеса и ради нее, собственно, софт и пишется.
А выполнение HTTP-запросов, взаимодействие с базой данных, запись и чтение файлов, все это обвязка, которая интегрирует логику в нужную среду и дает возможность эту логику запускать, сохранять и воспроизводить результаты ее работы. Два больших независимых и определяющих все слоя
Из этого правила следует, что код выполняющий побочные эффекты нужно располагать как можно выше по стеку вызовов. Идеальная цепочка: прочитали => вычислили => записали. Вычисление ничего не должно знать про окружающую среду.
3 минуты
20 июля 2024