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

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

Напишите понятный код и подумайте об ответственности

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

Первый пример. Ежедневно информационная система анализирует список подписчиков. Список подписчиков представляет собой csv-файл со следующими столбцами:

  • number’ - номер строки индекса.
  • «Email» - адрес электронной почты пользователя.
  • first_name’ - имя пользователя.
  • last_name’ - фамилия пользователя.
  • действие’ - действие пользователя. Он может содержать истину или ложь. Истина означает, что пользователь хочет подписаться. Значение false означает, что пользователь хочет отказаться от подписки.
  • subscription_ids’ - список идентификаторов подписок.

Нравится:

number|email|first_name|last_name|action|subscription_ids
1|some_email.ru|James|Smith|true|1,3
2|some_email2.ru|Barbara|O'connor|true|1,2,3,5
3|some_email3.ru|||false|3,6

Ниже приведен пример плохого кода. В нем много условий, и его трудно читать.

Давайте сделаем код более читабельным. Нам нужно добавить какой-нибудь метод в класс User и создать класс SubscriberRow. Это лучше.

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

  • день недели
  • количество проданной пиццы
  • сумма заказов
  • ТОП-3 покупателей (пользователи, у которых наибольшее количество заказов)
  • ТОП-3 проданных пиццы

Для этой задачи мы можем создать контроллер с одним общедоступным методом и любым частным методом вроде этого:

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

Что нам делать? Нам нужно определить ответственность и использовать DRY. Необходимо создать несколько классов для агрегирования данных и сбора ежедневных данных за неделю.

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

Не бойтесь использовать .fetch для хеширования

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

var = nil
if(some_hash.present? and some_hash[:key_1].present? and some_hash[:key_1][:key_2].present?)

  var = some_hash[:key_1][:key_2]
end

Для доступа к значению существует долгое условие. Трудно читать. Мы можем переписать его с помощью метода .fetch (). Этот метод хеширования принимает два аргумента: ключ и заполнитель. Если у хэша нет ключа, метод вернет заполнитель.

var = nil
if some_hash.present?
  var = some_hash.fetch(:key_1, {}).fetch(:key_2, nil)
end

Fetch имеет одну особенность: если существует хеш-ключ и он возвращает nil или false, метод вернет nil или ложь. Но если вы используете activesupport, вы можете переписать код в стиле rails.

var = some_hash.try(:fetch, :key_1, {}).try(:fetch, :key_2, nil)

Проверить переданные аргументы

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

Взгляните на пример. Мы создали класс GeoCoordinate для создания географических позиций на интерактивной карте.

class GeoCoordinate
  def initialize(latitude, longitude)
    @latitude = latitude
    @longitude = longitude
  end
  # and more code
end

Когда вы смотрите на код, вы видите, что метод инициализации принимает два аргумента: «широта» и «долгота». Но вы не видите, в каком экземпляре должны быть широта и долгота. Должна быть широта экземпляром числа или экземпляром строки? Разные разработчики могут писать разный код.

GeoCoordinate.new(55.45, 37.37)
GeoCoordinate.new('55.45', '37.37')
GeoCoordinate.new(['55.45'], ['37.37'])
GeoCoordinate.new(magicCoordinate.new)
GeoCoordinate.new(Latitude.new('55.45'), Longitude.new('37.37'))
GeoCoordinate.new(latitude: '55.45', longitude: '37.37’)

Вдобавок есть много других классов в разных частях системы.

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

Проверка возвращаемых значений

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

Например, принимает класс GeoCoordinate. Он имеет метод three_nearest_coordinates, который должен возвращать максимум три экземпляра класса GeoCoordinate . Но в текущей реализации он может возвращать array, экземпляр, false или nil. Это очень сложно отладить.

Нам нужно переписать three_nearest_coordinates. Он должен каждый раз возвращать массив.

Длинный хеш в переданных атрибутах недопустим

Любой метод / функция может принимать хеш в переданных атрибутах с 2–3 ключами. Звучит неплохо. Через некоторое время система становится больше, имеет больше функций, а в хеш-коде 8–10 ключей вместо 2–3. Это может создать проблемы. Разработчик может сделать орфографическую ошибку в ключах (неправильное написание трудно отладить), или выбор правильного параметра занимает много раз. Взгляните на пример.

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

Это все. Надеюсь, эти советы и практика будут вам очень полезны.