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

Урок № 2 о написании функций: все операторы внутри функции должны быть на одном уровне абстракции. Высокий уровень абстракции, вероятно, будет чем-то вроде `get-empty-cells` без раскрытия деталей реализации. С другой стороны, низкий уровень абстракции обрабатывал бы больше форматирования (например, добавление разрывов строк и т. д.), чем логику. Вероятно, это выглядело бы так:

(clojure.string/join “ | “ (cons “\n” (concat (apply concat (interpose [“\n”] (partition size formatted-board))) “\n”)))))

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

(defn mark-board-with-move 
   [board players player] 
   (let [move (player/get-move player board players)] 
       (player/make-move player board move)))

Написание небольших функций идет рука об руку с выбором хороших имен. Чем меньше функция, чем она более целенаправленна, тем проще подобрать описательное имя. Дядя Боб уверяет нас, что в длинном имени нет ничего плохого; если оно описательное, оно предпочтительнее короткого загадочного имени. Это также лучше, чем длинный описательный комментарий.

В случаях, когда мы используем операторы `switch`, должен быть интерфейс. В тех случаях, когда мы используем операторы `if`, в идеале должна быть одна строка кода, предпочтительно вызов функции. Обработка ошибок? Все абстрагируется в одну функцию. Да, мы говорим о этомуровне абстракции. Прочитав третью главу Чистого кода, посвященную функциям, я решил воспользоваться некоторыми из ее уроков, чтобы сердце.

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

Вот рефакторинговая версия кода. Я создал метод для каждой из этих ролей: `get-valid-size` проверяет правильность ввода, `run-size-loop` имеет блок try-catch, а `prompt-for-size` координирует запрос. пользователя.

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