Решения для предупреждения "слишком много параметров"
В некоторых моих методах есть Слишком много параметров и очень сложно поддерживать и читать
исходный код. И иногда меня беспокоит вопрос: "передают ли соответствующие значения в соответствующем порядке?"
Я использую Checkstyle как мой плагин Eclipse, и это дает мне предупреждение для более 7 параметров.
Я не уверен, что это может быть просто стандарт кодирования, и мне все равно. Но при передаче многих параметров с помощью представления, службы или дао я заметил, что его было очень трудно читать и трудно изменить позже раз.
Итак, я пытаюсь передать эти параметры с помощью...
-
Число Объектов или Beans. Но это дает мне еще одну проблему, потому что мои параметры не получат никакой гарантии (не уверены, присутствуют ли они или нет).
-
Параметры HashMap. Но это может заставить меня проверить некоторые проверки и попытаться сопоставить ключи со стороны метода-вызова.
Выше двух подходов может также потерять проверку ошибок во время компиляции.
Есть ли предложения по сокращению количества параметров?
Ответы
Ответ 1
Есть несколько методов, которые уменьшают количество параметров:
- Используйте минимизированные методы (разбивайте метод на несколько методов, каждый из которых требует только подмножество параметров)
- Использовать классы утилиты (вспомогательные классы) для хранения группы параметров (обычно статических классов-членов)
- адаптировать шаблон Builder от построения объекта к вызову метода.
- Попробуйте уменьшить поток данных между отдельными пакетами с лучшим архитектурным дизайном.
Обратитесь к стандартной стандартной java-книге;
- Java: как программировать
- Head First Java
- Эффективная Java
Также попробуйте изучить шаблоны дизайна, которые будут чрезвычайно полезны в качестве лучших методов кодирования.
- Образцы первых образцов дизайна
Ответ 2
Передача HashMap является распространенной практикой в нетипизированных языках сценариев, но является плохой практикой в Java. Это поражает преимущества сильной типизации, которая является частью того, как вы получаете производительность на Java. Другими словами, компилятор Java не сможет помочь вам выявить ошибки во время разработки, и вы, скорее всего, поймаете их во время выполнения.
Если параметры, которые вы передаете, концептуально связаны, вы можете указать, как они группируются в соответствующий объект. Например, если вы передаете такие параметры, как, например, firstName, lastName, dateOfBirth и т.д., Вместо этого вы можете передать объект Person, который имеет эти свойства. Это использует OO, чтобы сделать ваш проект легче думать и поддерживать.
Если я понимаю, что вы имеете в виду: "Но это дает мне другие проблемы, потому что мои параметры не получат никакой гарантии (не обязательно будет содержать или нет)", вы можете обеспечить гарантию, которая вам нужна, когда ваш Лицо или и т.д.. объект создается. Один из подходов заключался бы в использовании неизменяемого объекта Person (и т.д.): Нет сеттеров, вместо этого передавайте все параметры через конструктор. Бросьте IllegalArgumentException, если они не все правильно.
Удачи!
Ответ 3
Позвольте мне начать с некоторых предостережений относительно представленных предложений, прежде чем я представляю свои. Я беру на себя смелость пропускать "вонючие" решения - HashMap и beans, потому что вы можете четко видеть их недостатки.
Будьте осторожны с
-
Слепо использование вспомогательных классов для хранения групп параметров. Они могут действительно сиять, когда группа сплоченна (как в ответе Марка Филлипса), но они будут вызывать проблемы в противном случае (по существу, действуя точно так же, как типизированный HashMap). Я сомневаюсь, что они применили бы к проблеме передачи 7 параметров из представления в слой DAO.
-
Минимизированные методы также велики, когда они имеют смысл (например, в примере списка из эффективной Java-книги). Я редко вижу места, когда они это делают, поэтому я сомневаюсь, что они разрешат вашу проблему.
-
Шаблон Builder часто очень чистый, однако он решает проблему только одного слоя - он не говорит вам, как передавать параметры дальше. И когда у вас есть параметр из представления, который необходим в DAO, Builder просто раздувает ваш код, и вам все равно придется передавать параметры.
И прежде чем я, наконец, представим решение, позвольте мне оспаривать общее неявное предположение, что все данные должны передаваться через стек в виде параметров метода. Это справедливо только в том случае, если вы делаете свои объекты обработки приложениями или сеансами. Ограничение уходит, когда вы создаете все свои соответствующие объекты в начале обработки запроса. Затем вы можете использовать конструкторы объектов для передачи им только необходимой информации.
Оптимальный способ: использовать объекты жизненного цикла запроса
В некоторой степени это напоминает шаблон объекта метода или команды.
Чтобы применить это решение, вам нужно изменить точку входа в вашу систему - обычно это метод в каком-либо объекте уровня представления. Ответьте за две вещи:
- запрос создания графа объектов
- вызов метода запуска/запуска корня объекта
Первый шаг имеет решающее значение. Это место, где вы создаете объекты с областью запроса с каждого уровня: view, service и DAO. Для каждого из этих объектов вы передаете только необходимые данные своим конструкторам (например, если параметр "userIP" необходим только в DAO - скажем, для проверки доступа к БД, передайте его только объекту запроса DAO). Объектам запроса также нужны ссылки на их соавторов (например, служба, требующая DAO) - соответственно передайте их через конструктор.
Второй шаг: когда вы настроили свой графический объект, просто вызовите метод execute/run для первого из них (обычно это объект из уровня представления).
/** The example (in Scala) shows how your app entry point could look like.
* The presented method belongs to an app-scoped view-layer object.
*/
def delete(itemId: Id, userIP: IPAddress) {
// Note, that only RepositoryHelperReq class is interested in the
// "itemId" and "userIP" parameters
val repoReq = MainRepositoryReq(RepositoryHelperReq(itemId, userIP))
val serviceReq = MainServiceReq(ServiceHelperReq(repoReq))
val viewReq = MainViewReq(ViewHelperReq(serviceReq))
viewReq.execute()
}
Теперь позвольте мне ответить на некоторую ожидаемую критику этой картины.
Отрицание критики
-
Некоторые скажут, что производительность пострадает, потому что будет больше объекта в куче для сбора мусора.
Я бы спросил их для измерений, потому что обычно это не выделение объектов, это требует затрат, но удержание объектов (см. последняя презентация Саймона Риттера).
-
Некоторые будут спрашивать о данных в области приложения или сеанса, таких как источники данных или объект корзины покупок. Эти объекты все еще могут быть использованы - вы просто вводите их в объекты с запросом.
-
Некоторые будут критиковать структуру зависимостей, заявив, что представление должно зависеть только от обслуживания, а не от DAO. Это справедливое замечание, отметим только, что в классических веб-папках у вас все еще есть центральное место, которое зависит от каждого используемого слоя (место, часто называемое "концом света" ). Иногда это web.xml, иногда это контекст приложения Spring или модуль Guice. Если вы заботитесь о надлежащих зависимостях, я советую вам поместить всю логику factory в таком месте, чтобы позволить ей реализовать некоторый интерфейс уровня "Вид" и быть введенным в представление. Таким образом, вся ваша структура зависимостей останется чистой и модульной.
-
Некоторые скажут, что популярные рамки DI (главным образом Spring) поддерживают этот шаблон очень плохо. Это правда, вам нужно либо использовать приличную библиотеку DI (Guice, Dagger для Java или Macwire, если вы представляете Scala), либо подготовиться к борьбе с Spring, чтобы сделать это правильно.
Преимущества
- Отсутствие запаха с длинными параметрами
- Нет объектов, которые не связаны с запросом "запрос контекста" (Антипаттерн MagicContainer)
- Нет "штамп" -соединение- промежуточные слои не должны зависеть от переданных параметров, поэтому они могут быть освобождены независимо, более подвержены тестированию и более чистым.
- Данные используются только там, где это необходимо - более простое тестирование, менее насмешливое
- Может использоваться даже там, где сбой других методов, например, когда у вас нет связного параметра ParameterObject для извлечения или когда вы не можете легко разделить методы на минимальные ортогональные.
Кредиты
Решение этой конкретной проблемы было представлено мне Мишко Хевери в его отличном сообщении в блоге Как делать все неправильно с сервлетами и точно прибито вниз Управление объектами жизни (см. фрагмент "Больше распространенного нарушения" ). Я хочу поблагодарить его за его должности, потому что трудно найти точное руководство по этой конкретной проблеме в других источниках.
Ответ 4
Первый подход - это путь, это, по сути, инкапсуляция (один из основных принципов OO). Что касается "Но это дает мне еще одну неприятность, потому что мои параметры не получат никакой гарантии (не уверен, что они будут содержать или нет)". - это очень распространенная проблема и была рассмотрена с использованием JSR 303. Вот очень простой пример bean с аннотациями проверки JSR 303:
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class Book {
@NotNull
private String title;
@NotNull
private String author;
@Min(value=100)
private int numOfPages;
@NotNull
private String isbn;
... ... ...
}
Вот как это проверить:
Book book = new Book();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Book>> violations = validator.validate(book);
for (ConstraintViolation<Book> violation : violations) {
System.out.format("%s: %s%n",violation.getPropertyPath(), violation.getMessage());
}
И вот результат выполнения проверки:
isbn: may not be null
numOfPages: must be greater than or equal to 100
author: may not be null
title: may not be null
Дополнительная литература: http://www.jroller.com/eyallupu/entry/jsr_303_beans_validation_using
Вам не нужно выполнять такую ручную проверку, например, в Spring MVC вы можете поместить @javax.validation.Valid
в bean в метод, и он будет автоматически проверен.
Ответ 5
В общем, я бы сказал, попытайтесь уменьшить ответственность за метод/класс, чтобы уменьшить количество параметров. Но если они действительно нужны, не позволяйте плагину останавливать вас.
Ответ 6
Вы должны быть в состоянии решить эту проблему ниже.
1. Рисунок построителя
2. Псевдо-именованный параметрический подход будет хорошим способом сделать это (см. http://java.dzone.com/articles/named-parameters-java) (работает лучше всего для неизменяемых объектов)
3. Шаблон команды.
Вышеуказанные параметры действительны только в том случае, если вы используете все 7 параметров в своем методе.
если нет, есть недостаток в способе создания этого api.
я нашел ссылку ниже полезной, думая о разработке методов и api,
http://www.infoq.com/presentations/effective-api-design
Ответ 7
Вполне типично видеть конструкторы с длинными списками параметров, особенно для DAO. Очень часто я нахожу, что придерживаться аналогичных соглашений об именах для свойств может быть большой помощью для предотвращения этого.
Прошло довольно много времени с тех пор, как я написал любой Java-код, поэтому мне нужно будет передать конкретный пример java. В принципе, если у вас есть свойства с одинаковыми именами и назначаемыми типами, вы можете реализовать функцию клонирования, которая принимает любой объект и сопоставляет его с другим.
В С# я обсуждал это здесь: Лучший способ клонирования свойств разрозненных объектов
Что касается безопасности типов, вы контролируете свои соглашения об именах. Если personId всегда означает одно и то же, и всегда имеет тот же тип, вы избежите некоторых распространенных ошибок, которые могут вызвать проблемы в будущем.
Я был бы упущен, если бы не упомянул, что использование отражения не всегда является хорошим подходом. Вам нужно будет рассмотреть последствия для производительности. Еще одно соображение заключается в том, что вы проверяете полученный объект перед его сохранением. И, наконец, остерегайтесь мелких копий.
Ответ 8
Я не знаю, как ваши пакеты были разделены, а когда транспортные данные содержат слишком много параметров, мой совет - создать новый объект, который вызывает DTO (объект передачи данных) в javaee.As вы сказали, это непросто гарантировать. В противном случае использовать CALLBACK - лучший способ, который можно было бы легко гарантировать и расширить. Ничто не идеально, вы бы выбрали, какой аспект является вашим вниманием.
обратный вызов рассматривается как
void function(Callback callback){
callback.do();
}
Это мой совет, но не совсем правильный способ. Может быть, это полезно для вас.