В течение последних двух недель я комбинировал свой собственный HTTP-сервер (написанный на Java) и игру Крестики-нолики (построенную на Clojure). По сути, я хочу создать интерфейсную версию игры, основанную на API, обслуживаемом моим HTTP-сервером. Таким образом, интерфейс содержит только логику для получения ввода и рендеринга состояния игры.

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

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

Убедитесь, что ваши имена переменных и методов не лгут

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

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

При отладке позже я не должен был удивляться, когда вручную вызывал этот метод, и ничего не происходило. В конце концов, я написал этот крошечный метод несколько часов назад. Чтобы мой метод говорил правду, я сначала перенес проверку «помеченного» класса в метод с именем #shouldMakeMove. Этот ход имеет дополнительное преимущество, заключающееся в том, что запрос на перемещение основан на истинном, а не на ложном условии, что упрощает чтение всего метода. После этого изменения я изменил имя метода на #handleSpaceClick. Вместе эти изменения привели к тому, что метод выглядел так:

Чуть позже я добавил два новых условия в #shouldMakeMove. Каждое дополнительное условие снижает вероятность того, что метод действительно отправляет запрос. Было бы вопиющей ложью сказать себе (и всем, кто полагался на код), что метод действительно сделал запрос.

Напишите методы с небольшим количеством (или без) аргументов

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

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

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

Хотя решение объединить все классы промежуточного ПО в один параметр упростило мою жизнь по созданию HTTP-сервера, я не переставал думать о том, как кто-то (то есть я), использующий мой сервер, реализует промежуточное ПО. Возможно, они не захотят или не будут нуждаться в поддержке Logger, Authenticator или CORS. Что приводит меня к следующему пункту:

Сделать необязательные аргументы необязательными

В рамках разработки моего Web Tic Tac Toe, мой первый рассказ о том, как просто запускать сервер, используя только бэкэнд, код Clojure. Я намеренно не стал ничего настраивать для Tic Tac Toe. Однако из-за того, как я настроил свой класс Server, мне пришлось расширить экземпляр Middleware и передать его конструктору Server. Этот обходной путь излишне замедлил мой процесс, потому что созданный мной пример класса промежуточного программного обеспечения просто возвращал все, что он принимал.

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

Блог о вашем прогрессе

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

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