Вернемся к основам этого шаблона.

Отказ от ответственности. Этот пост был написан с учетом интерфейсных проектов, таких как веб-сайты, мобильные приложения React Native и приложения Electron. Хотя есть некоторые навыки, которые можно перенести в бэкэнд-программирование, как я упомяну ниже. Кроме того, название немного шуточное. 😄 Это не так сложно, но об этом часто забывают.

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

«Вы, должно быть, шутите, да? Организация файлов? Такая базовая концепция, не могу поверить, что этот парень пишет об этом…» Что ж, я воздержусь и скажу, что это считается настолько простым, что его часто упускают из виду и забывают, и может привести к созданию файлов «бога» огромного размера, заполненных десятками и десятками функций в долгосрочной перспективе. Так что подумайте о том, чтобы прочитать этот пост! И уважайте основы!

Два шаблона организации кода

Рассмотрим два способа организации функций вашего кода:

  1. Группировка нескольких связанных функций в один файл. Пример: файл с именем math.js в папке utils/, заполненный функциями add, subtract, multiply и divide:
utils
└── math.js
^ contains functions add, subtract, multiply, and divide
  1. Размещение каждой функции в отдельном файле. Пример: файлы add.js, subtract.js, multiply.js, divide.js, все в папке utils/math/:
utils
└── math
    ├── add.js
    ├── divide.js
    ├── multiply.js
    └── subtract.js

Какое-то время я думал, что вариант 1 лучше. Организация связанных функций в одном файле! Изысканный! Кажется, это имеет смысл, верно? Меньше файлов, меньше сложности?

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

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

Три причины для файлов с одной функцией

1. Тесты

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

2. Слияние кода

Если у вас есть сложное дерево git с функциями и ветвями исправлений, размещение многих функций в большом файле «utils» или подобном приведет только к головной боли, когда вы хотите сохранить функциональность (без каламбура) каждой из функций. Когда каждая из ваших функций изолирована в одном файле, гораздо проще просмотреть историю git для каждого файла и увидеть, какие изменения были внесены в данную функцию (и только эту функцию).

3. Организация

Легче разобраться в организации вашей кодовой базы, если сразу понять, где функция основана на одном только операторе import. При варианте 1 функция add находится в собственном одноименном файле, как и subtract и т. д. При варианте 2 требуется дополнительный умственный скачок, чтобы перейти от мысли "хорошо... utils/, math.js... ага! add находится в пределах math.js!"

Если вы пишете какое-либо современное приложение, имеющее хоть какую-то сложность, поверьте мне: у вас будет множество служебных функций, собранных вокруг различных действий, задач и активностей — легко к северу от 50 или даже 100 функций, если вы правильно разделяете свой код.

Недостатки

У этого шаблона не так много недостатков — я могу вспомнить только один, а именно то, что ваших операторов import (или require) будет больше, если вы разделите каждую из своих функций на отдельные файл. Однако большинство линтеров и средств форматирования в любом случае будут создавать многострочные import, если вы выберете вариант 1. выше, то есть форматирование, подобное следующему:

import {
add,
subtract,
multiple,
divide
} from "./utils/math"

Вместо схемы организации (вариант 2.), которую я предлагаю:

import { add } from "./utils/math/add"
import { subtract } from "./utils/math/subtract"
import { multiply } from "./utils/math/multiply"
import { divide } from "./utils/math/divide"

И на самом деле, это очередь короче для всех вас, гончих за очередями 😉

Другие соображения

Обратите внимание, что этот шаблон предназначен для вашего собственного внутреннего кода. Если вы отправляете библиотеку или пакет, имеет смысл сделать все ваши экспорты из одного и того же файла, например, и index.js или index.ts. Даже тогда это только для интерфейса экспорта и исходный код самой библиотеки мог бы (и, на мой взгляд, должен) использовать этот паттерн.

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

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

Спасибо!

Как вам нравится организовывать свои функции в клиентских приложениях? Вы бы выбрали вариант 1 или 2 и почему?

Ваше здоровье,

-Крис 🍺