Стильный код и его последствия

Стильный код и его последствия

Abanazar


Вы никогда не задумывались о том, что во время написания кода наш мозг использует и комбинирует два стиля? Там где мы устали — декларативный, а там где полны фантазий — императивный.


Давайте для наглядного примера напишем алгоритм вычисления факториала числа в обоих стилях на Golang:

func factorialImperative(n int) int {

result := 1

for i := 1; i <= n; i++ {

result *= i

}

return result

}


func factorialDeclarative(n int) int {

if n <= 1 {

return 1

}

return n * factorialDeclarative(n-1)

}


Во втором подходе мы используем рекурсивную функцию для определения факториала числа. Он является декларативным, так как описывает что такое факториал (рекурсивное умножение на число) без явного указания как это делать в цикле.


Отличие между этими двумя стилями проявляется в способе описания логики. В императивном стиле мы явно управляем состоянием и циклами, тогда как в декларативном стиле мы определяем логику и позволяем ей самой себя вызывать.


Императивный стиль:

* Описывает как выполнить определенную задачу шаг за шагом.

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

* Часто использует циклы, условия и изменение состояния переменных.

Пример: использование циклов for и операторов if для управления выполнением задачи.


Декларативный стиль:

* Описывает что должно быть достигнуто, но не обязательно как это должно быть сделано.

* Программист определяет цель, а не шаги, необходимые для достижения этой цели.

* Основной акцент на выразительности и абстракциях.

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

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


Теперь давайте обратимся к Python для ещё одного наглядного примера. Мы преобразуем числа из одного списка в новый список квадратов этих чисел.


Императивный стиль (как):

numbers = [1, 2, 3, 4, 5]

squared_numbers = []

for num in numbers:

squared = num * num

squared_numbers.append(squared)



Декларативный стиль (что):

numbers = [1, 2, 3, 4, 5]

squared_numbers = list(map(lambda x: x * x, numbers))


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


Во-первых не всем всегда бывает понятна лаконичная абстракция по типу той же lambda функции.

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


Это создаёт некоторые проблемы даже в кодовой коммуникации в команде разработчиков. Не все коллеги в вашей команде понимают такие "речевые обороты" и абстракции. А те что понимают всё равно потратят время в раздумьях. Всё это не является какой-то острой проблемой, но я приверженец императивного стиля везде где это возможно. Я считаю что код, который в каждом своем фрагменте придерживается одного стиля является более понятным и читабельным чем смесь и/или перепады стилей. Но если мы возьмём полностью декларативный код с абсолютной "лаконичностью" и использованием синтаксического сахара то получим намного менее читабельный рассказ. Согласитесь, что легче воспринимать инструкции КАК нежели ЧТО. Компьютеру нет разницы, а вот для человека она весомая.


И ведь проблема то не на одном синтаксисе топчется, но ещё и на знаниях и понимании. Если разработчик не знает работы так сказать из под капота он так и продолжит везде пихать свои декларативные элементы. Этот стиль порой даже заставляет нас не знать, не понимать и бездумно применять. Всё это перестает быть индивидуальной проблемой с опытом конечно, но доставляет неприятностей к этому моменту.


Под декларативный стиль за счёт абстракции можно подписать и импортируемые библиотеки. Кто нибудь из вас открывал исходный код стандартной библиотеки net/http на Golang? А я вот открыл и удивился очевидному уровню абстрактной вложенности, ведь даже этот в какой-то мере низкоуровневый код импортирует другие модули. Мы бездумно год за годом на этом своем backend используем методы сетевых библиотек даже не зная как они работают и что делают на самом деле. Да, мы знаем теорию, но поверьте там внизу скрыт целый мир, который работает далеко не так очевидно как мы считаем. А что происходит глобально в индустрии? Каждый день кто-то пишет новые библиотеки и фреймворки, а мы их бесконечно потребляем, разработчики языков дают нам больше сахара и мы его тоже употребляем в больших количествах.


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


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

Report Page