контекст
Недавно я заинтересовался созданием лучшего отформатированного кода. И лучше я имею в виду «следование правилам, одобренным достаточным количеством людей, чтобы считать это хорошей практикой» (поскольку, конечно, никогда не будет одного уникального «лучшего» способа кодирования).
В настоящее время я в основном пишу на Ruby, поэтому я начал использовать линтер (Rubocop), чтобы предоставить мне некоторую информацию о «качестве» моего кода (это «качество» определяется проектом сообщества ruby-style-guide ).
Обратите внимание , что я буду использовать «качество» , как в «качестве форматирования», не столько об эффективности коды, даже если в некоторых случаях эффективность коды на самом деле является зависит от того, как написан кода.
Во всяком случае, делая все это, я понял (или, по крайней мере, вспомнил) несколько вещей:
- Некоторые языки (особенно Python, Ruby и т. Д.) Позволяют создавать отличные однострочные коды
- Следование некоторым рекомендациям для вашего кода может сделать его значительно короче и, тем не менее, очень четким
- Тем не менее, строгое соблюдение этих рекомендаций может сделать код менее понятным / легким для чтения.
- Кодекс может почти полностью соответствовать некоторым рекомендациям и при этом быть низкого качества.
- Удобочитаемость кода в основном субъективна (например, «то, что я нахожу понятным, может быть совершенно неясным для коллег-разработчика»)
Это всего лишь наблюдения, а не абсолютные правила, конечно. Вы также заметите, что читаемость кода и следующие рекомендации могут показаться не связанными на данном этапе, но здесь рекомендации являются способом сузить число способов переписать один кусок кода.
Теперь несколько примеров, чтобы прояснить все это.
Примеры
Давайте рассмотрим простой пример использования: у нас есть приложение с User
моделью " ". У пользователя есть необязательный firstname
и surname
и обязательный email
адрес.
Я хочу написать метод " name
", который потом вернет name ( firstname + surname
) пользователя, если хотя бы его firstname
или surname
присутствует, или егоemail
в качестве запасного значения, если нет.
Я также хочу, чтобы этот метод принял " use_email
" в качестве параметра (логическое значение), позволяя использовать электронную почту пользователя в качестве запасного значения. Этот use_email
параметр " " должен по умолчанию (если не передан) как "true
".
Самый простой способ написать это в Ruby:
def name(use_email = true)
# If firstname and surname are both blank (empty string or undefined)
# and we can use the email...
if (firstname.blank? && surname.blank?) && use_email
# ... then, return the email
return email
else
# ... else, concatenate the firstname and surname...
name = "#{firstname} #{surname}"
# ... and return the result striped from leading and trailing spaces
return name.strip
end
end
Этот код является наиболее простым и понятным способом сделать это. Даже для того, кто не «говорит» на Ruby.
Теперь давайте попробуем сделать это короче:
def name(use_email = true)
# 'if' condition is used as a guard clause instead of a conditional block
return email if (firstname.blank? && surname.blank?) && use_email
# Use of 'return' makes 'else' useless anyway
name = "#{firstname} #{surname}"
return name.strip
end
Это короче, все еще легко понять, если не легче (пункт охраны более естественен для чтения, чем условный блок). Охранное положение также делает его более совместимым с руководящими принципами, которые я использую, так что выиграйте здесь. Мы также снижаем уровень отступа.
Теперь давайте используем магию Ruby, чтобы сделать ее еще короче:
def name(use_email = true)
return email if (firstname.blank? && surname.blank?) && use_email
# Ruby can return the last called value, making 'return' useless
# and we can apply strip directly to our string, no need to store it
"#{firstname} #{surname}".strip
end
Еще короче и прекрасно следуют рекомендациям ... но гораздо менее понятно, поскольку отсутствие выражения возврата делает его немного запутанным для тех, кто не знаком с этой практикой.
Именно здесь мы можем начать задавать вопрос: стоит ли оно того? Должны ли мы сказать «нет, сделать его читабельным и добавить»return
« »(зная, что это не будет соответствовать рекомендациям). Или мы должны сказать: «Все в порядке, это путь Руби, учите этот чертов язык!»?
Если мы выберем вариант B, то почему бы не сделать его еще короче:
def name(use_email = true)
(email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end
Вот он, однострочник! Конечно, это короче ... здесь мы используем тот факт, что Ruby вернет значение или другое в зависимости от того, какое из них определено (поскольку электронная почта будет определена при тех же условиях, что и раньше).
Мы также можем написать это:
def name(use_email = true)
(email if [firstname, surname].all?(&:blank?) && use_email) || "#{firstname} #{surname}".strip
end
Он короткий, не такой сложный для чтения (я имею в виду, мы все видели, как может выглядеть уродливый однострочник), хороший Ruby, он соответствует руководству, которое я использую ... Но, тем не менее, по сравнению с первым способом написания это гораздо менее легко читать и понимать. Также можно утверждать, что эта строка слишком длинная (более 80 символов).
Вопрос
Некоторые примеры кода могут показать, что выбор между «полноразмерным» кодом и многими его сокращенными версиями (вплоть до знаменитой однострочной) может быть сложным, поскольку, как мы видим, однострочные могут быть не такими страшными, но тем не менее, ничто не сравнится с «полноразмерным» кодом с точки зрения читабельности ...
Так вот реальный вопрос: где остановиться? Когда коротко, достаточно коротко? Как узнать, когда код становится «слишком коротким» и менее читаемым (имея в виду, что он довольно субъективен)? И даже больше: как всегда соответствующим образом кодировать и избегать смешивания однострочников с «полноразмерными» кусками кода, когда мне просто хочется?
TL; DR
Главный вопрос здесь: когда дело доходит до выбора между «длинным, но ясным, читаемым и понятным фрагментом кода» и «мощным, более коротким, но трудным для чтения / понимания однострочным», зная, что эти два - верх и дно шкалы, а не два единственных варианта: как определить, где находится граница между «достаточно ясно» и «не так ясно, как должно быть»?
Главный вопрос не в классической «Однострочник против читабельности: какой из них лучше?» но "Как найти баланс между этими двумя?"
Редактировать 1
Комментарии в примерах кода предназначены для «игнорирования», они здесь для того, чтобы прояснить, что происходит, но не должны приниматься во внимание при оценке читабельности кода.
return
добавленным ключевым словом . Эти семь символов добавляют немного ясности в мои глаза.
[firstname,surname,!use_email].all?(&:blank?) ? email : "#{firstname} #{surname}".strip
... потому что false.blank?
возвращает true и троичный оператор сохраняет вам несколько символов ... ¯ \ _ (ツ) _ / ¯
return
должно добавить ключевое слово ?! Он не предоставляет никакой информации вообще . Это чистый беспорядок.