Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails
Code

Основы рефакторинга кода с запашком на Ruby/Rails. Часть 01

by
Length:LongLanguages:
This post is part of a series called Ruby / Rails Code Smell Basics.
Ruby/Rails Code Smell Basics 02

Russian (Pусский) translation by Marat Amerov (you can also view the original English article)

file

Темы

  • Основы
  • Сопротивление
  • Большой класс / Божественный класс
  • Извлечение класса
  • Длинный метод
  • Длинный список параметров

Основы

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

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

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

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

Итак, о чем мы говорим, когда люди упоминают «код с запашком»? Это всегда проблема в коде? Не обязательно! Можете ли вы полностью их избежать? Я так не думаю! Вы имеете в виду «код с запашком», приводящий к поломке кода? Ну, иногда, а иногда и нет. Должно ли их исправление быть приоритетом? Тот же ответ, я боюсь: иногда да, а иногда вы, конечно, должны сначала обжарить большую рыбу. Вы ненормальный? Справедливый вопрос на этом этапе!

Прежде чем продолжить погружение в этот весь бизнес, не забудьте одно: не пытайтесь исправить каждый «код с запашком», с которым вы сталкиваетесь — это, безусловно, пустая трата времени!

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

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

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

Сопротивление

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

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

Большой класс / Божественные классы

Начнем с чего-то фантастический звучащего — «Божественные классы», я думаю, что их особенно легко понять новичкам. Божественные классы - это особый случай «кода с запашком», называемого Большими классами. В этом разделе я расскажу об обоих. Если вы потратили немного времени на Rails, вы, вероятно, видели их так часто, что они выглядят нормальными для вас.

Вы наверняка помните мантру «толстые модели, тонкие контроллеры»? Ну, на самом деле, тонкий, это хорошо для всех этих классов, но, я полагаю, как правило, это хороший совет для новичков.

Божественные классы — это объекты, которые поглощают всевозможные знания и поведение, как черная дыра. Ваши обычные предположения чаще всего включают модель пользователя и любую проблему (надеюсь,!), Которую ваше приложение пытается решить - в первую очередь, по крайней мере. Приложение todo может состоять из модели Todos, приложение для покупок на Products, приложение для фотографий на Photos - вы получаете тенденцию.

Люди называют их «божественными классами», потому что они слишком много знают. У них слишком много связей с другими классами — в основном потому, что кто-то лениво их моделировал. Однако тяжелая работа заключается в том, чтобы держать «божественные классы» под контролем. Они легко поддаются снятию с них большой обязанности и как свидетельствуют многие греческие герои, требуется разделить и победить «богов».

Проблема с ними заключается в том, что их становится все труднее и труднее понять, особенно для новыми членами команды, их сложнее изменить, и их повторное использование становится все менее и менее доступным. О да, вы правы, ваши тесты излишне сложнее писать. Короче говоря, на самом деле нет причины к тому, чтобы иметь большие классы и, в частности, «божественные классы».

Есть пара общих симптомов/признаков того, что вашему классу нужны какие-то изменения:

  • Вам нужно много скроллить!
  • Имеется тонна приватных методов?
  • В вашем классе есть семь или более методов?
  • Трудно сказать, что делает ваш класс на самом деле - в краткой форме!
  • У вашего класса есть много причин для изменения, когда ваш код развивается?

Кроме того, если вы прищуриваетесь глядя на свой класс и думаете: «а? Фу!» вы так-же можете что-то предпринять. Если все это звучит знакомо, есть хорошие шансы, что вы нашли себе прекрасный образец.

Уродливо, да? Можете ли вы увидеть, сколько здесь гадости? Конечно, я положил немного вишни сверху, но рано или поздно вы столкнетесь с таким кодом. Давайте подумаем о том, какие обязанности этот класс CastingInviter должен выполнять.

  • Отправка электронной почты
  • Валидация сообщений и адресов электронной почты
  • Избавление от пробелов
  • Разделение адресов электронной почты на запятые и точки с запятой

Следует ли все это сбрасывать на класс, который просто хочет доставить кастинг через deliver? Конечно нет! Если ваш метод внесения приглашений изменится, вы можете рассчитывать на то, что вы столкнетесь с некоторыми операциями с дробовиком. CastingInviter не должен знать большинстве этих деталей. Это больше зависит от класса, который специализируется на работе с материалами, связанными с электронной почтой. В будущем вы найдете много причин изменить здесь код.

Извлечение класса

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

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

Вы создаете новый класс, и постепенно перемещаете функциональные возможности один за другим. Перемещайте каждый метод отдельно и переименуйте их, если на это есть причина. Затем укажите новый класс в оригинале и делегируйте необходимые функции. Хорошо, что у вас есть покрытие тестами (надеюсь!), Которое позволяет вам проверять, все ли работает на каждом шагу. Стремитесь к тому, чтобы повторно использовать извлеченные классы. Это легче понять на практике, поэтому давайте прочитаем код:

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

Здесь мы делегируем методы новому классу, который специализируется на доставке этих приглашений по электронной почте. У вас есть одно определенное место, которое проверяет правильность сообщений, приглашений и их доставку. CastingInviter не нужно ничего знать об этих деталях, поэтому мы делегируем эти обязанности новому классу CastingEmailHandler.

Знание того, как доставлять и проверять достоверность этих писем с приглашением, теперь содержится в нашем новом извлеченном классе. У нас теперь больше кода? Стоит ли разделять код на области ответственности? Будьте уверенны! Можем ли мы выйти за рамки этого и реорганизовать CastingEmailHandler? Абсолютно! Не в чем себе не отказывайте!

В случае, если вы волнуетесь о valid? метод в CastingEmailHandler и CastingInviter, это заготовка для RSpec, которая нужна для создания пользовательского матчера. Это позволяет написать что-то вроде:

Мне кажется, это очень удобно.

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

Нет никакого конкретного рецепта для решения этих задач — это всегда зависит от предпочтений. Инкрементные методы рефакторинга лучше всего соответствуют этим задачам. Я знаю, это немного расстраивает время от времени. Однако, если следовать принципу единой ответственности (SRP), это будет долгий путь, и это хороший путь.

Длинный метод

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

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

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

spec/features/some_feature_spec.rb

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

spec/features/some_feature_spec.rb

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

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

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

Длинный список параметров

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

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

Давайте рассмотрим этот простой пример. M может назначить новую миссию и ему нужно имя миссии, агент и цели. M также может переключать статус агента double 0, что означает лицензию на убийство.

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

Таким образом, мы создали новый объект, Mission, который сосредоточен исключительно на предоставлении M информации, необходимой для назначения новой миссии и предоставления #assign_new_mission с уникальным параметром объекта. Не нужно передавать их в виде надоедливых параметров. Вместо этого вы сообщаете объекту, чтобы он раскрывал необходимую информацию внутри самого метода. Кроме того, мы также извлекли некоторое поведение — информацию о том, как печатать новый объект Mission.

Зачем M нужно знать о том, как печатать назначения миссий? Новый #assign также выиграл от извлечения, потеряв некоторый вес, потому что нам не нужно было передавать объект параметра, поэтому не нужно писать такие вещи, как mission.mission_name, mission.agent_name и т.д. Теперь мы просто используем наш attr_reader(s), который намного чище, чем без извлечения. Вы углубляетесь?

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

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

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

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

Теперь я собираюсь посмотреть нового Бонда. Слышал, что он не так хорош, хотя...

Обновление: Saw Specter. Мой вердикт: по сравнению с Skyfall, который был не очень имхо — Spectre, был wawawiwa!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.