Недавно я создал HTTP-сервер на Java, и часть того, на чем я сосредоточился, - это сделать сервер расширяемым, чтобы можно было легко создавать ответы с любым количеством компонентов.

Основной поток HTTP-сервера состоит в том, что он сначала прослушивает входящее соединение на данном сокете. Когда соединение присутствует, сервер принимает входящий запрос и преобразует его в объект Request, чтобы упростить внутреннюю обработку. Затем в зависимости от типа запроса (GET, POST и т. Д.) И запрошенной конечной точки создается ответ.

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

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

Мы можем решить всю эту неразбериху с помощью шаблона Builder, любезно предоставленного Бандой четырех. В этом посте мы рассмотрим мой исходный (беспорядочный) код, который создает объект Response для моего сервера, чтобы увидеть проблемы, которые могут привести к желанию использовать шаблон построителя. Затем мы проведем рефакторинг, включив шаблон, и посмотрим, насколько чище создается наш ответ.

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

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

По сути, это выглядит как вложение класса Builder внутри класса, который представляет сложный объект, который мы пытаемся построить.

Исходный код

Вот отправная точка для рефакторинга. В зависимости от входящего запроса исходящий ответ может принимать любое количество различных форм, особенно в том, что касается разных типов заголовков. Посмотрите, как Response ниже имеет три разных конструктора: один для ответа с StatusLine, один с StatusLine и Headers, а третий для StatusLine, Headers и Body. Это «работает», но могло быть намного лучше. Создавая экземпляр нового запроса, вы должны одновременно держать в уме несколько вещей, что создает возможность для совершения ошибок. Также обратите внимание, что в ResponseBuilder есть много дублирования, поскольку это связано с созданием заголовка.

Пример создания ответа с использованием приведенного выше кода будет выглядеть примерно так:

Отредактированный код

Сначала мы переместим ResponseBuilder в Response как внутренний класс и переименуем его в Builder. Затем мы устанавливаем конструктор Response в частный, чтобы новый ответ можно было создать только с вложенным Builder.

До:

Response response = new Response(statusLine, headers, body)

После:

Response response = new Response.Builder(statusLine)
                          .withHeader("Location", "localhost:5000")
                          .withHeader("Allow", "GET,HEAD")
                          .withBody("Response body")

Посмотрите, насколько это чище! Кроме того, мы можем добавить столько полей заголовков, сколько захотим, добавив новый .withHeader() для каждого желаемого поля. Ниже представлен реорганизованный класс Response с вложенным классом Builder. Обратите внимание на то, что конструктор для Response является частным, так что новый объект ответа может быть создан только путем доступа к внутреннему Builder. Это защитит нас от случайной попытки создать ответ без использования шаблона построителя. Этот подход может добавить немного больше сложности заранее, но преимущества определенно стоят затраченных усилий, особенно если вы собираетесь создавать много разных типов одного и того же объекта. Теперь у нас есть настраиваемый и расширяемый способ создания Response объектов, и наш код стал намного более читабельным и выразительным.