Давайте рассмотрим принципы, которые помогут писать более качественный код.
Термины
Для начала дадим несколько определений:
состояние (state) — это данные, хранящиеся в памяти приложения. Каждая назначенная переменная является частью состояния приложения;
рефакторинг (refactor) — изменение кода программы без изменения ее поведения (по крайней мере, видимого пользователю). Цель рефакторинга — упростить код и сделать его проще для чтения и понимания.
1. Разделение проблем
Давайте сравним создание кода с процессом готовки. Простой рецепт предполагает, что каждый последующий шаг будет совершен после завершения предыдущего, как только все шаги будут выполнены — блюдо будет готово. Но если взять, например, сложный рецепт, когда на плите одновременно стоят две кипящие кастрюли, в микроволновке вращается тарелка, у вас есть три вида овощей, мельницы с разными специями (и вы не помните, что уже добавили), это вызовет настоящий стресс.
Кроме того, на кухне есть еще один повар, который то усложняет, то упрощает процесс. Нужно тратить время на координацию, передавая вещи туда и обратно, сражаться за место за плитой и все время регулировать температуру в духовке. Чтобы делать все это, нужна практика.
Если знать, что на кухне будет несколько поваров, тогда стоит разделить рецепт на несколько субрецептов. Тогда каждый из поваров будет работать только над своей частью рецепта с минимальным взаимодействием. Один — готовит кипящую воду для макарон, второй — нарезает и готовит овощи, третий — измельчает сыр. Четвертый — делает соус. Благодаря четко определенным задачам каждый из них делает свою работу.
Худший вариант написания кода равен самому простому рецепту, когда каждая строчка кода определена в одном и том же пространстве и нанизана сверху вниз. Чтобы понять или изменить такой код, вам нужно будет пересмотреть его много раз. Ведь переменная из второй строчки может влиять на операцию в строке 832, и единственный способ это понять — пересмотреть весь код.
Более хороший вариант написания кода похож на пример со вторым поваром. Некоторые операции передаются другим частям программы, что частично упрощает код, но не приводит к его упорядочению. Это, конечно, улучшение, но этого недостаточно.
Лучший из вариантов — это разделение рецепта на субрецепты. В коде их называют «модулями» или «классами». Каждый модуль связан с определенной операцией или частью данных. Таким образом, человек, занимающийся овощами, не будет беспокоиться об ингредиентах для соуса, а человек, готовящий макароны, не будет волноваться о терке для сыра. Их проблемы разделены.
Польза от такого разделения очевидна. Предположим, что программисту нужно изменить некоторые данные программы позже — сделать ее свободной от глютена для клиентов с целиакией или добавить сезонный овощ. Ему нужно будет внимательно посмотреть код и изменить одну небольшую часть. Если весь код, связанный с овощами, содержится в одном небольшом классе с минимальным интерфейсом, программисту не нужно будет беспокоиться о том, что добавление овоща испортит соус.
Суть в том, что для внесения каких-либо изменений программисту нужно будет думать только о небольших частях программы, а не обо всех сразу.
2. Глобальные переменные
Возьмем, например, переменную username. При создании формы авторизации в приложении вы вдруг решили, что имя пользователя будет использоваться в нескольких местах приложения, например, на странице авторизации и в настройках. Поэтому сделали эту переменную глобальной. В Python она обозначается как global, а в JavaScript это свойство объекта window. На первый взгляд, это хорошее решение. Теперь в любом месте, где вам нужно использовать переменную username, вы легко можете это сделать. Но почему не сделать глобальными все переменные?
Представим, что что-то пошло не так. Нашелся баг в коде, который связан с переменной username. Даже с использованием инструментов поиска в вашей IDE найти эту переменную будет сложно. При поиске переменной username вы получите сотни, а то и тысячи результатов. Некоторые результаты поиска будут глобальной переменной, которую вы установили в начале проекта, некоторые — другими переменными с именем username, некоторые будут просто словом username, которое использовалось в комментарии, имени класса, имени метода и т. д. Можно будет установить дополнительные фильтры для поиска, но все же отладка займет много времени.
Решение проблемы заключается в том, чтобы поместить username в контейнер (например, класса или объекта данных), который вводится или передается в качестве аргумента классам и методам, нуждающимся в нем. Контейнер также может хранить любые подобные данные, связанные с входом в систему (только не пароль пользователя). Также можно сделать этот контейнер неизменяемым, например, при установке имени пользователя оно уже не может быть изменено. Это упростит отладку, даже если имя пользователя используется десятки тысяч раз.
Такое использование кода облегчит вашу жизнь. Нужные данные всегда будут находиться только в одном месте. И если вам понадобится отследить, когда часть кода или данных была изменена, вы всегда сможете использовать getter или setter.
