Запахи кода - это определенные закономерности и характеристики кода, которые могут указывать или создавать более серьезные проблемы в будущем. Другими словами, код сомнительного качества.

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

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

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

В конце статьи есть TL; DR, если вам нужны только основные моменты и извлеченные уроки.

Кодовые запахи против антипаттернов

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

Запах кода означает, что шаблон проектирования может использоваться не по назначению или есть возможность реализовать что-то лучше. Это скорее возможность, чем факт.

Рефакторинг

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

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

Но как мы можем что-то изменить, не изменяя его поведения? Если нам нужно провести рефакторинг компонента, чтобы использовать меньше свойств или разделить его на несколько более мелких компонентов, считается ли это изменением поведения?

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

Раздутые компоненты

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

Когда эта практика переносится в приложения React, это приводит к созданию главного компонента. Это компоненты, которые могут содержать до 1000 строк кода и содержат безумное количество логики и разметки.

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

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

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

Как этого избежать?

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

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

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

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

Когда вы начнете отделять компоненты от мастера, вы увидите, что существует логика, относящаяся только к определенным частям страницы. Если вы извлечете несколько таких частей, контейнер станет намного компактнее.

Повторяющаяся логика

Когда дело касается повторяемости, я следую простому правилу. Я не могу точно вспомнить, где я это читал, но там сказано, что если вам нужно скопировать что-то в третий раз, то это нужно извлечь.

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

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

Как этого избежать?

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

Здесь мы должны поговорить о том, чтобы не чрезмерно изобретать и чрезмерно усложнять проблемы. Если есть часть бизнес-логики, которая требуется во многих местах, переместите ее в файл utils.js. Если у вас возникнет необходимость связать его с разметкой или каким-либо конкретным поведением, можно продвигать его и использовать шаблоны, упомянутые выше.

Буровая установка

Слишком большое понимание концепции умных и глупых (презентационных и контейнерных) компонентов может привести к раздутым контейнерам, но это также может привести к печально известной буровой установке.

Prop Drilling означает прохождение реквизита нескольких уровней компонентов в глубину, чтобы достичь определенного компонента, которому требуются эти реквизиты. А поскольку родительский элемент не может передать свойство напрямую одному из своих внуков, он должен пройти через другие компоненты на своем пути вниз.

Проблема в том, что все остальные компоненты на самом деле не будут использовать опору, они просто передадут ее на более низкий уровень. Это приводит к ненужному раскрытию информации и более тесной связи в кодовой базе. Изменение структуры данных или имени свойства потребует внесения изменений в нескольких местах.

Часто свойство компонента в компоненте переименовывается из-за коллизии имен или ради ясности. Выполнение этого пару раз в цепочке сверления пропеллеров приведет нас только в замешательство и разочарование.

Как этого избежать?

Если вы работаете над небольшим приложением и бурение опор становится для вас проблемой, то использование Context API может быть вашим лучшим решением. Начиная с React 16.3 и далее у нас есть доступ к новому и улучшенному API, который позволяет нам обмениваться данными в глубоких иерархиях компонентов.

Если управление состоянием и поддержка поддержки начинают выходить из-под контроля, то добавление в стек таких библиотек, как Redux или MobX, должно облегчить проблему. Конечно, они требуют значительного обучения, поэтому избегайте поспешных решений.

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

Поскольку и Context, и библиотеки, о которых я упоминал, имеют отличную документацию, нет смысла добавлять здесь пример.

JSX вне метода рендеринга

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

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

Как этого избежать?

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

Контраргумент здесь заключается в том, что, добавляя компонент вместо вызова метода внутри JSX, мы все еще не можем точно понять, что он делает. Хотя это правда, у нас будет четкое представление о принятых свойствах компонента, что является сильным намеком на его функциональность. По крайней мере, мы будем знать, какие данные ему нужны и какие обратные вызовы они вызывают.

Глубокое вдавливание

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

Честно говоря, я стараюсь избегать этого, независимо от того, с каким языком или фреймворком я работаю. На мой взгляд, плоский и линейный код намного проще для понимания и приятнее для глаз.

Как этого избежать?

Если мы хотим уменьшить уровень вложенности, мы можем легко это сделать, увеличив уровень абстракции.

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

Отступы внутри методов класса также могут увеличиваться очень быстро. Изменение стиля кода может легко решить эту проблему. Простое удаление ненужных операторов else и выравнивание длинных цепочек обещаний с помощью async / await может иметь большое влияние.

Отсутствие четкого стандарта

Экосистема JavaScript динамична. Разные члены сообщества придерживаются разных мнений и склонны их придерживаться.

Часто к команде присоединяются разработчики с разными стилями кодирования и привычками. Хотя иногда разнообразие может привести к лучшим решениям, часто эти культурные противоречия приводят к низкому качеству кода. Различные парадигмы, стили кодирования и форматирование делают нашу базу кода похожей на React-эквивалент космополитического города из научного сообщества. Fi фильм.

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

Как этого избежать?

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

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

TL;DR

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

Новостная рассылка

Привет, я веду небольшой информационный бюллетень, в котором делюсь случайными мыслями, размышлениями и идеями о разработке программного обеспечения. Ни руководств, ни рекламы. Я считаю, что некоторые вещи, о которых мне стоит задуматься, доставляются в ваш почтовый ящик каждые несколько недель. Если вам это кажется привлекательным, вы можете подписаться на ЗДЕСЬ.

Хлопайте и делитесь

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