Это плохая практика, чтобы заставить сеттер возвращать «это»? - PullRequest
226 голосов
/ 28 августа 2009

Это хорошая или плохая идея, чтобы сеттеры в java возвращали "this"?

public Employee setName(String name){
   this.name = name;
   return this;
}

Этот шаблон может быть полезен, потому что тогда вы можете связывать сеттеры следующим образом:

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

вместо этого:

Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);

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

Обновление:

То, что я описал, очевидно справедливо, но я действительно ищу некоторые мысли о том, является ли это в целом приемлемым, и есть ли какие-либо подводные камни или связанные с ними передовые практики. Я знаю о шаблоне Builder, но он немного сложнее, чем то, что я описываю - как описывает его Джош Блох, для создания объекта существует связанный статический класс Builder.

Ответы [ 27 ]

98 голосов
/ 28 августа 2009

Это неплохая практика. Это все более распространенная практика. Большинство языков не требуют от вас иметь дело с возвращаемым объектом, если вы не хотите, чтобы он не изменял синтаксис «нормального» использования сеттера, но позволял вам связывать его вместе.

Это обычно называется шаблоном компоновщика или плавным интерфейсом .

Это также распространено в Java API:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();
82 голосов
/ 02 сентября 2009

Подведем итог:

  • это называется "плавным интерфейсом" или "цепочкой методов".
  • это не "стандартная" Java, хотя в наши дни вы все больше ее видите (прекрасно работает в jQuery)
  • он нарушает спецификацию JavaBean, поэтому будет работать с различными инструментами и библиотеками, особенно сборщиками JSP и Spring.
  • это может помешать некоторым оптимизациям, которые JVM обычно делает
  • некоторые люди думают, что он очищает код, другие думают, что это «ужасно»

Пара других пунктов, не упомянутых:

  • Это нарушает принцип, что каждая функция должна делать одну (и только одну) вещь. Вы можете или не можете в это поверить, но я думаю, что в Java это работает хорошо.

  • IDE не собираются создавать их для вас (по умолчанию).

  • И наконец, вот точка данных реального мира. У меня были проблемы с использованием библиотеки, созданной следующим образом. Конструктор запросов Hibernate является примером этого в существующей библиотеке. Так как методы set * в Query возвращают запросы, невозможно определить, просто используя сигнатуру, как ее использовать. Например:

    Query setWhatever(String what);
    
  • Он вводит неоднозначность: изменяет ли метод текущий объект (ваш шаблон) или, возможно, Query действительно неизменный (очень популярный и ценный шаблон), и метод возвращает новый. Это просто затрудняет использование библиотеки, и многие программисты не используют эту функцию. Если бы сеттеры были сеттерами, было бы понятнее, как их использовать.

75 голосов
/ 20 декабря 2010

Для этого я предпочитаю использовать методы with:

public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
  setFoo(foo);
  return this;
}

Таким образом:

list.add(new Employee().withName("Jack Sparrow")
                       .withId(1)
                       .withFoo("bacon!"));
72 голосов
/ 28 августа 2009

Не думаю, что с этим что-то конкретно не так, это просто вопрос стиля. Это полезно, когда:

  • Вам нужно установить сразу несколько полей (в том числе при строительстве)
  • вы знаете, какие поля необходимо установить во время написания кода, и
  • существует множество различных комбинаций полей, которые вы хотите установить.

Альтернативой этому методу может быть:

  1. Один мега-конструктор (недостаток: вы можете передавать множество нулей или значений по умолчанию, и становится трудно понять, какое значение чему соответствует)
  2. Несколько перегруженных конструкторов (недостаток: становится громоздким, если у вас их несколько)
  3. Фабричные / статические методы (обратная сторона: то же самое, что перегруженные конструкторы - становится громоздким, если их несколько)

Если вы собираетесь установить только несколько свойств за раз, я бы сказал, что не стоит возвращать 'this'. Конечно, он падает, если позже вы решите вернуть что-то еще, например, индикатор состояния / успеха / сообщение.

25 голосов
/ 28 августа 2009

Если вы не хотите возвращать 'this' из установщика, но не хотите использовать второй параметр, вы можете использовать следующий синтаксис для установки свойств:

list.add(new Employee()
{{
    setName("Jack Sparrow");
    setId(1);
    setFoo("bacon!");
}});

В качестве отступления я думаю, что в C # он немного чище:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});
11 голосов
/ 13 мая 2015

Он не только нарушает соглашение о методах получения / установки, но также нарушает структуру ссылок на методы Java 8. MyClass::setMyValue - это BiConsumer<MyClass,MyValue>, а myInstance::setMyValue - это Consumer<MyValue>. Если ваш установщик возвращает this, то он больше не является действительным экземпляром Consumer<MyValue>, а скорее Function<MyValue,MyClass> и приведет к тому, что что-либо, использующее ссылки на методы на эти установщики (при условии, что они являются пустыми методами), сломается.

7 голосов
/ 28 августа 2009

Я не знаю Java, но я сделал это на C ++. Другие люди говорили, что это делает строки очень длинными и очень сложными для чтения, но я делал это много раз:

list.add(new Employee()
    .setName("Jack Sparrow")
    .setId(1)
    .setFoo("bacon!"));

Это даже лучше:

list.add(
    new Employee("Jack Sparrow")
    .Id(1)
    .foo("bacon!"));

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

6 голосов
/ 28 августа 2009

Поскольку он не возвращает void, он больше не является допустимым установщиком свойств JavaBean. Это может иметь значение, если вы один из семи человек в мире, использующих визуальные инструменты Bean Builder, или один из 17, использующих элементы JSP-bean-setProperty.

6 голосов
/ 28 августа 2009

Эта схема (предназначенная для каламбура), называемая «плавным интерфейсом», сейчас становится довольно популярной. Это приемлемо, но на самом деле это не моя чашка чая.

5 голосов
/ 28 августа 2009

Как минимум в теории , это может повредить механизмы оптимизации JVM, устанавливая ложные зависимости между вызовами.

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

Вот почему я голосую "нет", не используйте его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...