Подтвердить что ты не робот

Java-конструктор с большими аргументами или Java bean метод getter/setter

Я не могу решить, какой подход лучше для создания объектов с большим количеством полей (10+) (все обязательные) подход конструктора геттера/сеттера. Конструктор, по крайней мере, вы обеспечиваете, чтобы все поля были установлены. Java Beans легче видеть, какие переменные устанавливаются вместо огромного списка. Шаблон-компоновщик НЕ представляется здесь подходящим, поскольку все поля являются обязательными, и строитель требует, чтобы вы поместили все обязательные параметры в конструктор-конструктор.

Спасибо, D

4b9b3361

Ответ 1

Лучший подход (imho) - использовать какой-то строитель:

MyClass a = new MyClassBuilder().blah("blah").foo("foo").doStuff().toMyClass();

где MyClass по-прежнему immutable, но имеет гораздо более читаемое создание, чем конструктор с 10 аргументами.

Это также называется свободным интерфейсом . Джош Блох ссылается на это в Эффективная Java.

Ответ 2

Моя первая мысль - проверить правильность модели инкапсуляции. Наличие 10 + обязательных полей звучит довольно много, и, возможно, имеет смысл иметь более мелкозернистые компоненты в этом сценарии?

Связаны ли некоторые из этих полей/параметров? Могут ли они быть объединены в объекты, которые имеют смысл (например, x-coordinate и y-coordinate объединены в объект Point и т.д.).

Ответ 3

В своей книге Code Complete Стив Макконнелл утверждает, что ни одна процедура не должна иметь больше, чем максимум 6, может быть, 7 аргументов. Большинство из этих заявлений - это не только его мнение, но и подкрепленное исследованиями, например. ошибок, связанных с структурой кода.

Clean Code от Robert Martin идет еще дальше: он рекомендует 1 или 2 аргумента, а 3 уже считается "запахом кода". Лично я считаю, что Clean Code немного экстремален в своих местах, но в целом он дает хорошие аргументы.

"Целая группа параметров" (как бы там ни было много), представляет собой проект "кухонного раковины" с большим количеством запоздалых идей и небольшой структуры. Это также делает техническое обслуживание более сложным и подверженным ошибкам; по крайней мере, он делает код трудным для чтения.

Все это дает веские основания думать о сокращении числа параметров. Другие ответы предложили некоторые практические предложения.

Ответ 4

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

OP был обновлен, чтобы отклонить шаблон построителя, но, похоже, он основан на недоразумении. Тот факт, что шаблон Builder существует, не удаляет принудительное выполнение всех параметров.

Рассмотрим следующий объект:

 public class SomeImmutableObject {
      private String requiredParam1;
      private String requiredParam2;
      //etc.

      private SomeImmutableObject() { //cannot be instantiated outside the class }

      public static class Builder {
          private SomeImmutableObject instance;
          public Builder() { instance = new SomeImmutableObject();
          public Builder setParameter1(String value) {
               instance.requiredParam1 = value;
               return this;
          }
          //etc for each parameter.

          public SomeImmutableObject build() {
             if (instance.requiredParam1 == null || instance.requiredParam2 == null /*etc*/)
                throw new IllegalStateException("All required parameters were not supplied.");
             return instance;
          }
      } 
 }

Обратите внимание, что вы можете выполнить в основном одно и то же, сделав приватный пакет полей и поместив его в один и тот же пакет.

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

Таким образом, для всех заявленных требований шаблон Builder работает просто отлично. Тот факт, что требуются все 10 параметров, вовсе не дисквалифицирует шаблон Builder. Если есть какая-то другая потребность, которую шаблон не удовлетворяет, пожалуйста, уточните.

Edit: OP добавил комментарий (довольно давно, но я только что получил ответ на этот вопрос, поэтому я только видел его сейчас) с интересным вопросом: как вы проверяете примитив в более поздний момент времени?

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

     private Double doubleForPrimitive;

     public Builder setDouble(double d) {
         doubleForPrimitive = d;
     }

     public SomeImmutableObject build() {
         if(doubleForPrimitive != null) {
               instance.doubleParam = doubleForPrimitive;
         } else {
                throw new IllegalArgumentExcepion("The parameter double was not provided");
         }
         //etc.
     }

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

Ответ 5

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

Widget widge = new Widget.Builder(). manufacturer("333").serialNumber("54321").build();

Ответ 7

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

Ответ 8

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

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

Ответ 9

Я бы реализовал шаблон построителя следующим образом:

package so1632058;

public class MyClass {
  private final String param1;
  private final String param2;

  MyClass(Builder builder) {
    this.param1 = builder.param1;
    this.param2 = builder.param2;
  }

  public String getParam1() {
    return param1;
  }

  public String getParam2() {
    return param2;
  }

  @SuppressWarnings("hiding")
  public static final class Builder {
    String param1;
    String param2;

    public Builder param1(String param1) {
      this.param1 = param1;
      return this;
    }

    public Builder param2(String param2) {
      this.param2 = param2;
      return this;
    }

    public MyClass toMyClass() {
      return new MyClass(this);
    }
  }
}

И затем используйте следующий код:

package so1632058;

public class Main {

  public static void main(String[] args) {
    MyClass.Builder builder = new MyClass.Builder();
    builder.param1("p1").param2("p2");
    MyClass instance = builder.toMyClass();
    instance.toString();
  }

}

Некоторые примечания:

  • Нет методов со многими параметрами.
  • Дополнительная проверка может быть выполнена в конструкторе MyClass.
  • Я сделал прозрачность видимости конструктора, чтобы избежать предупреждения "синтетического доступа".
  • То же самое для полей экземпляра компоновщика.
  • Единственный способ создать экземпляр MyClass - через Builder.

Ответ 10

Действительно зависит от конкретного класса. Должна ли она быть неизменной? Это простой объект значения без какого-либо поведения? Собираетесь ли вы сопоставить этот объект значения с параметрами веб-службы или с реляционной базой данных? Вы собираетесь сериализовать его? (Некоторым из этого материала нужен конструктор по умолчанию). Можете ли вы рассказать немного больше об объекте?

Ответ 11

Существуют ли варианты класса, которые могут принимать меньше аргументов, или есть только один, и у него есть десять свойств?

Является ли объект неизменным?

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

Ответ 12

Если все параметры действительно обязательны, то я не вижу причин, почему бы не использовать конструктор. Однако, если это не так, то использование построителя кажется наилучшим подходом.
Полагаться только на сеттеры - это, на мой взгляд, худшие решения, поскольку нет ничего, чтобы обеспечить соблюдение всех обязательных свойств. Конечно, если вы используете проводку Spring Framework bean или другое подобное решение, тогда Java beans отлично работает, как вы можете проверить после инициализации все, что было установлено.

Ответ 13

Это вопрос дизайна без сомнения. Вы должны легко считывать удобочитаемость. Десять конструкторов arg проще, но могут быть или не быть более удобочитаемыми/поддерживаемыми. Он также имеет меньше вызовов методов для включения и выключения стека вызовов. Установка десяти различных значений через сеттеры является более читабельным и явным. Это не обязательно "проще" (хотя вы можете спорить в обоих направлениях), и добавляет больше вызовов методов для включения и выключения стека вызовов.

Но вот еще несколько моментов, о которых нужно подумать. Даже с помощью десяти аргумента-конструктора вы могли бы попросить программиста выбрать нулевой, пустой, ложный или нулевой (в зависимости от объекта или примитива), который может быть или не быть тем, что вы намеревались. Единственный способ контролировать это - это потенциально вызвать исключение в конструкторе. Это то, что вы действительно хотите сделать? Это поведение, которое вы ожидаете?

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

Наконец, я бы спросил, есть ли что-то, что вам нужно для дефолта? Поскольку, если определенные вещи могут по умолчанию, вы можете либо установить их значения по умолчанию на уровне класса, либо установить их по умолчанию в конструкторе (в зависимости от ваших идеалов программирования и которые, по вашему мнению, более конкретны и помогают с поведением вашего объекта), Затем вы можете "предустановить" определенные поля и должны только переопределить их через сеттеры вручную или через сеттеры через Builder.

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

Ответ 14

IMO конструкторы не создают хороший API при создании объектов, особенно когда количество аргументов велико и однотипны.

new Person(String, String, String, String); // what the?? this is what may 
                                            // show up in IDEs

где он фактически означает Person (имя, фамилия, имя экрана, пароль, (например, сакэ)

Как упоминает cletus, шаблон Builder с цепочкой хорош. Другим преимуществом с конструктором является то, что , если объекты неизменяемы, строитель может вернуть тот же объект (в этом случае вы можете иметь частный конструктор пакета с 15 аргументами, об этом знает только строитель). Строители также могут возвращать любой подтип объектов, которые они строят

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

У нас была аналогичная проблема в нашем проекте. Нам приходилось получать определенные значения из домашнего шлюза (нашего продукта). Он поддерживал запрос-ответ, основанный на запросах XML-протокол через http. Но создание объекта Request для отправки в пользовательский интерфейс было утомительным, с настройкой запросов obejcts с соответствующими параметрами и фильтрами и т.д.

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

Request r = new Request("SET");
r.setAction("add"); // modify, add, delete
r.setModuleName("NPWIFI"):
r.addParameter(new Param("wifiAclMac", "aa:bb:cc:dd:ee:ff"));
r.addParameter(new Param("wifiCommit", "commit"));
r.setActionParam("wifiAclRowStatus")
r.addFilter(new Filter(Type.EQUALS, "wifiInterface", "wl0"));
r.addFilter(new Filter(Type.EQUALS, "wifiAclMac", "yourgateway"));

Resonse r = gwSession.sendRequest(r);

Таким образом, мы изменили его на внутреннюю DSL, которая имела такой же SQL-интерфейс, но только программный

Query q = DQL.add("wifiAclMac", "wifiCommit").into("NPWIFI").values
            ("aa:bb:cc:dd:ee:ff", "commit").withAp("wifiAclRowStatus")
            .where("wifiInterface").is("wl0")
            .and("wifiAclMac").is("aa:bb:cc:dd:ee:ff").end();

Объект DQL "построитель запросов" сделал все здание с проверкой и также оказалось очень удобным в использовании.

Builders и DSL - это элегантное и мощное средство создания и создания объектов, но посмотрите, что имеет смысл в вашем случае.

Ответ 15

Это довольно сложно ответить абстрактно. Что действительно нужно сделать, так это посмотреть на эти десять параметров и посмотреть, что они собой представляют. Я рассматриваю их как ключевые вопросы:

  • Могут ли некоторые из них быть объединены в объекты более высокого уровня? Например, переменные X и Y могут быть объединены в Point. У нас было много таких ситуаций в рамках программы маршрутизации грузов, где все поля были смоделированы как примитивные строки. Введение нескольких концепций более высокого уровня действительно помогло сделать его читаемым.
  • Могут ли некоторые параметры быть "по умолчанию" определенными значениями?
  • Являются ли они действительно независимыми, ортогональными понятиями? Я работал над множеством систем и никогда не видел, чтобы это было правдой. Если нет, есть некоторые мысли, чтобы сделать с ними.

Ответ 16

Я бы избегал конструкторов с большим количеством аргументов. Классы с большим количеством аргументов в конструкторах могут быть громоздкими. Представьте, что у вас есть наследственная иерархия с подклассами, каждая из которых имеет множество аргументов в своих конструкторах. Сколько будет работы, если потребуется, изменить аргументы somes классов верхнего уровня.

Я бы передал интерфейс вашему контруктору, который вы могли бы изменить, не нарушая код, или использовать подход Javabeans и не иметь конструктора arg.

Ответ 17

Могут ли ваши поля объединяться в промежуточный объект? Например, если вы передаете 10 полей, которые описывают человека, тогда создайте объект PersonInfo для передачи этих данных. Я лично предпочитаю передавать во всех обязательных полях при создании объекта. Таким образом, вы не получите наполовину испеченный объект, который неизбежно будет использоваться и подвергаться насилию.

Ответ 18

Если все параметры действительно необходимы, и вы не используете Spring для создания экземпляра bean, я бы обязательно пошел с конструктором с 10 параметрами. Ясно, что все параметры действительно необходимы.

Если вы создаете Spring (возможно, иногда) для создания bean, или вам не очень нравится иметь много временных переменных в методе, который создает этот bean, вы также можете использовать геттеры и сеттера. Если вы также применяете метод validate для проверки правильности объекта, у вас не должно быть проблем.

Метод проверки лучше работает, если вы используете его последовательно; возможно, имея интерфейс Validatable. Он очень хорошо подходит для рабочего процесса, если он является частью стиля программирования. Если есть только один или два класса, в которых используется эта парадигма, это, вероятно, не стоит. Вы забудете позвонить validate() в любом случае где-нибудь.

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

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