Лучшая практика для передачи многих аргументов в метод? - PullRequest
78 голосов
/ 12 марта 2010

Иногда нам приходится писать методы, которые получают много аргументов, например:

public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}

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

Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;

......

public void doSomething(Map<Object,Object> params)
{
 // extracting params 
 Object objA = (Object)params.get("objA");
 ......
 }

Это не очень хорошая практика, инкапсуляция параметров в карту - это пустая трата эффективности. Хорошая вещь, чистая подпись, легко добавлять другие параметры с наименьшими изменениями. Какова наилучшая практика для такого рода проблем?

Ответы [ 15 ]

119 голосов
/ 12 марта 2010

В Эффективная Java , Глава 7 (Методы), пункт 40 (Тщательно подписи методов проектирования), Блох пишет:

Существует три метода сокращения слишком длинных списков параметров:

  • разбить метод на несколько методов, каждый из которых требует только подмножества параметров
  • создание вспомогательных классов для хранения группы параметров (обычно это статические классы-члены)
  • адаптировать шаблон Builder от конструирования объекта к вызову метода.

Для получения более подробной информации, я призываю вас купить книгу, она действительно того стоит.

67 голосов
/ 12 марта 2010

Использование карты с магическими строковыми ключами - плохая идея. Вы теряете любую проверку времени компиляции, и действительно неясно, каковы требуемые параметры. Вам нужно написать очень полную документацию, чтобы восполнить это. Помните ли вы через несколько недель, что это за строки, не глядя на код? Что делать, если вы сделали опечатку? Использовать неправильный тип? Вы не узнаете, пока не запустите код.

Вместо этого используйте модель. Создайте класс, который будет контейнером для всех этих параметров. Таким образом вы сохраняете безопасность типов Java. Вы также можете передать этот объект другим методам, поместить его в коллекции и т. Д.

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

23 голосов
/ 13 марта 2010

Если у вас много необязательных параметров, вы можете создать свободный API: замените один метод цепочкой методов

exportWithParams().datesBetween(date1,date2)
                  .format("xml")
                  .columns("id","name","phone")
                  .table("angry_robots")
                  .invoke();

Используя статический импорт, вы можете создавать внутренние свободно распространяемые API:

... .datesBetween(from(date1).to(date2)) ...
12 голосов
/ 12 марта 2010

Это называется «Ввести объект параметра». Если вы обнаружите, что передаете один и тот же список параметров в нескольких местах, просто создайте класс, который содержит их все.

XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);

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

Конечно, это общая философия, и, поскольку вы не предоставили никаких подробностей, я также не могу дать вам более подробный совет. : -)

10 голосов
/ 12 марта 2010

Во-первых, я бы попытался изменить метод. Если он использует столько параметров, он может быть слишком длинным. Если разбить его, это улучшит код и потенциально сократит количество параметров для каждого метода. Вы также можете иметь возможность реорганизовать всю операцию в свой собственный класс. Во-вторых, я бы искал другие случаи, когда я использую тот же (или расширенный) из того же списка параметров. Если у вас есть несколько экземпляров, это, вероятно, означает, что эти свойства принадлежат друг другу. В этом случае создайте класс для хранения параметров и используйте его. Наконец, я бы оценил, стоит ли использовать число параметров для создания объекта карты для улучшения читабельности кода. Я думаю, что это личный вызов - с этим решением каждый раз сталкивается с болью, и где компромисс может отличаться. По шести параметрам я бы, наверное, этого не сделал. За 10 я бы, наверное, (если бы ни один из других методов не сработал первым).

7 голосов
/ 17 сентября 2014

Это часто проблема при построении объектов.

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

Вы также можете адаптировать его к вызову метода.

Это также значительно улучшает читаемость.

public class BigObject
{
  // public getters
  // private setters

  public static class Buider
  {
     private A f1;
     private B f2;
     private C f3;
     private D f4;
     private E f5;

     public Buider setField1(A f1) { this.f1 = f1; return this; }
     public Buider setField2(B f2) { this.f2 = f2; return this; }
     public Buider setField3(C f3) { this.f3 = f3; return this; }
     public Buider setField4(D f4) { this.f4 = f4; return this; }
     public Buider setField5(E f5) { this.f5 = f5; return this; }

    public BigObject build()
    {
      BigObject result = new BigObject();
      result.setField1(f1);
      result.setField2(f2);
      result.setField3(f3);
      result.setField4(f4);
      result.setField5(f5);
      return result;
    }
  }
}

// Usage:
BigObject boo = new BigObject.Builder()
  .setField1(/* whatever */)
  .setField2(/* whatever */)
  .setField3(/* whatever */)
  .setField4(/* whatever */)
  .setField5(/* whatever */)
  .build();

Вы также можете поместить логику проверки в методы Builder set .. () и build ().

6 голосов
/ 12 марта 2010

Существует шаблон, называемый Объект параметра .

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

5 голосов
/ 12 марта 2010

Вы можете создать класс для хранения этих данных. Должен быть достаточно значимым, но гораздо лучше, чем использовать карту (OMG).

4 голосов
/ 15 марта 2010

Code Complete * предлагает несколько вещей:

  • «Ограничьте количество параметров процедуры до семи. Семь - магическое число для понимания людей» (стр. 108).
  • «Поместить параметры в порядок ввода-изменения-вывода ... Если несколько подпрограмм используют одинаковые параметры, разместите аналогичные параметры в согласованном порядке» (стр. 105).
  • Поместите переменные состояния или ошибки в последнюю очередь.
  • Как уже упоминалось tvanfosson , передайте только те части структурированных переменных (объектов), которые нужны программе. Тем не менее, если вы используете большую часть структурированной переменной в функции, просто передайте всю структуру, но имейте в виду, что это в некоторой степени способствует соединению.

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

2 голосов
/ 12 марта 2010

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

Более чистым способом было бы сгруппировать все параметры в объектном бине, но это все еще не решает проблему полностью.

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

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

...