Сейчас я поставлю своего капитана Очевидный Кейп и вкратце напишу, как избавиться от методов с множеством параметров, особенно если некоторые параметры являются необязательными или как-то зависят друг от друга (например, идут парами, один исключает другой и т. Д.) ,
Давайте возьмем простой класс RestClient
. Необходимо иметь возможность передавать целевой ресурс, необязательные заголовки HTTP, метод HTTP, тело запроса, тайм-ауты и т. Д. Все они, кроме URL, являются необязательными. Как вы пишете этот класс?
Первое, что приходит в голову, это перегрузка:
1
2
3
4
|
public <T> T request(String url); public <T> T request(String url, HttpMethod method, Object body); public <T> T request(String url, HttpMethod method, Object body, Map<String, String> headers); public <T> T request(String url, HttpMethod method, Object body, Map<String, String> headers, long timeout); |
Хороший? Нет. Потому что вы можете вызвать URL с помощью GET, указав тайм-аут, но не указав тело или заголовки. И всякий раз, когда должен быть добавлен новый параметр (например, для аутентификации), вы должны скопировать все методы и добавить параметр. Проще говоря: вы не можете и не должны иметь перегруженный метод для каждой возможной комбинации параметров.
Как мы должны подойти к проблеме? Есть несколько способов , и я предпочитаю использовать что-то вроде шаблона Builder . Предполагается, что шаблон Builder используется для конструирования объектов и замены конструкторов, принимающих несколько аргументов. Но его философия может быть перенесена на вышеуказанную проблему. Например:
1
|
public <T> T request(RestCall call); |
Что такое RestCall? Это изменяемый объект, который вы конфигурируете, желательно с свободным интерфейсом , чтобы сделать ваш вызов покоя. Все поля имеют геттеры, так что RestClient
может получить все поля, в которых нуждается. Использование может выглядеть так:
1
2
|
client.request(RestCall.to(url).withMethod(HttpMethod.POST) .withBody(requestObject).withTimeout( 5000 )); |
Он читается как проза, он позволяет вам использовать только один (или очень мало) методов вместо перегрузки для каждой возможной комбинации, и в то же время позволяет вам иметь каждую возможную комбинацию параметров. Метод «to (..)» является простым фабричным методом . Пример реализации метода withX выглядит следующим образом:
1
2
3
4
|
public RestCall withBody(Object body) { this .body = body; //assign a private field return this ; } |
Вы также можете иметь разумные значения по умолчанию для каждого поля. В этом случае, метод GET, предопределенный тайм-аут по умолчанию.
Этот подход также дает вам возможность указать ограничения. Например, вы не должны разрешать установку тела для запросов GET. Так:
1
2
3
4
5
6
7
8
|
public RestCall withBody(Object body) { if (method == HttpMethod.GET) { throw new IllegalStateException( "Body not supported for GET" ); } this .body = body; //assign a private field return this ; } |
И если два параметра идут вместе, например, вы хотите добавить заголовки один за другим, тогда у вас может быть addHeader(String, String)
.
Как правило, этот подход более читабелен и его легче расширять и поддерживать.