Неужели плохая практика заставит сеттера вернуть «это»?

Это хорошая или плохая идея, чтобы сделать сеттеры в java возвратить «это»?

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, но это немного больше связано с тем, что я описываю, – как говорит Джош Блох, существует связанный с ним статический class Builder для создания объекта.

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

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

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

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

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

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

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

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

 String s = new StringBuilder().append("testing ").append(1) .append(" 2 ").append(3).toString(); 

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

 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!")); 

Обобщить:

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

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

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

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

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

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

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

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

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

 list.Add(new Employee() { Name = "Jack Sparrow", Id = 1, Foo = "bacon!" }); 

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

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

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

Это еще лучше:

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

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

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

Это совсем не плохая практика. Но это не совместимо с JavaBeans Spec .

И многие спецификации зависят от этих стандартных аксессуаров.

Вы всегда можете заставить их сосуществовать друг с другом.

 public class Some { public String getValue() { // JavaBeans return value; } public void setValue(final String value) { // JavaBeans this.value = value; } public String value() { // simple return getValue(); } public Some value(final String value) { // fluent/chaining setValue(value); return this; } private String value; } 

Теперь мы можем использовать их вместе.

 new Some().value("some").getValue(); 

Вот еще одна версия для неизменяемого объекта.

 public class Some { public static class Builder { public Some build() { return new Some(value); } public Builder value(final String value) { this.value = value; return this; } private String value; } private Some(final String value) { super(); this.value = value; } public String getValue() { return value; } public String value() { return getValue();} private final String value; } 

Теперь мы можем это сделать.

 new Some.Builder().value("value").build().getValue(); 

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

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

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

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

Я сторонник сеттеров, которые «это» возвращаются. Меня не волнует, не совместим ли это с фасолью. Для меня, если это нормально, выражение expression / statement = =, то сеттеры, возвращающие значения, прекрасны.

Раньше я предпочитал этот подход, но я решил отказаться от него.

Причины:

  • Читаемость. Это делает код более удобочитаемым для каждого setFoo () на отдельной строке. Вы обычно читаете код много, много раз, чем один раз, когда вы его пишете.
  • Побочный эффект: setFoo () должен устанавливать только поле foo, ничего больше. Возвращаясь, это лишний «ЧТО было».

Образец Builder, который я видел, не использует соглашение setFoo (foo) .setBar (bar), но больше foo (foo) .bar (bar). Возможно, именно по этим причинам.

Это, как всегда, дело вкуса. Мне просто нравится подход «наименьших неожиданностей».

Этот конкретный шаблон называется методом Chaining. Wikipedia link , у этого есть больше объяснений и примеров того, как это делается на разных языках программирования.

PS: Просто подумал оставить его здесь, так как я искал конкретное имя.

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

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

 public class NutritionalFacts { private final int sodium; private final int fat; private final int carbo; public int getSodium(){ return sodium; } public int getfat(){ return fat; } public int getCarbo(){ return carbo; } public static class Builder { private int sodium; private int fat; private int carbo; public Builder sodium(int s) { this.sodium = s; return this; } public Builder fat(int f) { this.fat = f; return this; } public Builder carbo(int c) { this.carbo = c; return this; } public NutritionalFacts build() { return new NutritionalFacts(this); } } private NutritionalFacts(Builder b) { this.sodium = b.sodium; this.fat = b.fat; this.carbo = b.carbo; } } 

Paulo Abrantes предлагает другой способ сделать JavaBean seters бегло: определить внутренний class строителя для каждого JavaBean. Если вы используете инструменты, которые сбрасываются сеттерами, возвращающими значения, шаблон Пауло может помочь.

На первый взгляд: «Честно!».

О дальнейшей мысли

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

на самом деле меньше подверженности ошибкам, чем

 Employee anEmployee = new Employee(); anEmployee.setName("xxx"); ... list.add(anEmployee); 

Настолько интересно. Добавление идеи в панель инструментов …

Да, я думаю, что это хорошая идея.

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

 class People { private String name; public People setName(String name) { this.name = name; return this; } } class Friend extends People { private String nickName; public Friend setNickName(String nickName) { this.nickName = nickName; return this; } } 

Это будет работать:

 new Friend().setNickName("Bart").setName("Barthelemy"); 

Это не будет принято Eclipse! :

 new Friend().setName("Barthelemy").setNickName("Bart"); 

Это происходит потому, что setName () возвращает «Люди», а не «Друг», и нет «PeoplesetNickName».

Как мы можем написать сеттерам, чтобы вернуть class SELF вместо имени classа?

Что-то вроде этого будет хорошо (если бы существовало ключевое слово SELF). Это все равно существует?

 class People { private String name; public SELF setName(String name) { this.name = name; return this; } } 

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

Из заявления

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

я вижу две вещи

1) Бессмысленное утверждение. 2) Отсутствие читаемости.

Это может быть менее читаемым

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

или это

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

Это более читаемо, чем:

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

Я делаю свои сеттеры довольно долгое время, и единственная реальная проблема связана с библиотеками, которые придерживаются строгих getPropertyDescriptors, чтобы получить bean access / bean accessor. В этих случаях ваш «bean» java не будет иметь нареканий, которых вы ожидаете.

Например, я не проверял его наверняка, но я не удивлюсь, что jackson не узнает их как сеттеров при создании вами java-объектов из json / maps. Надеюсь, я ошибаюсь в этом вопросе (я скоро проверю).

На самом деле, я разрабатываю легкую SQL-ориентированную ORM, и мне нужно добавить некоторый код beyong getPropertyDescriptors к признанным сеттерам, который возвращает это.

Давно ответ, но мои два цента … Это прекрасно. Я желаю, чтобы этот свободный интерфейс использовался чаще.

Повторение переменной «factory» не добавляет больше информации ниже:

 ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(Foo.class); factory.setFilter(new MethodFilter() { ... 

Это чище, имхо:

 ProxyFactory factory = new ProxyFactory() .setSuperclass(Properties.class); .setFilter(new MethodFilter() { ... 

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

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

Этот подход позволяет использовать ваш код клиента:

  • Безразлично к типу возврата
  • Легче поддерживать
  • Избегайте побочных эффектов компилятора

Вот несколько примеров.

 val employee = Employee().apply { name = "Jack Sparrow" id = 1 foo = "bacon" } val employee = Employee() with(employee) { name = "Jack Sparrow" id = 1 foo = "bacon" } val employee = Employee() employee.let { it.name = "Jack Sparrow" it.id = 1 it.foo = "bacon" } 

Если я пишу API, я использую «return this» для установки значений, которые будут установлены только один раз. Если у меня есть другие значения, которые пользователь сможет изменить, вместо этого я использую стандартный установщик void.

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

Я согласен со всеми плакатами, утверждающими, что это нарушает спецификацию JavaBeans. Есть причины сохранить это, но я также чувствую, что использование этого шаблона Builder (о котором было упомянуто) имеет свое место; если он не используется повсюду, он должен быть приемлемым. «Это место», для меня, является конечной точкой вызова метода «build ()».

Конечно, есть другие способы установки всех этих вещей, но преимущество в том, что оно позволяет избежать 1) многопараметрических публичных конструкторов и 2) частично определенных объектов. Здесь у вас есть сборщик, который собирает все необходимое, а затем вызывает его «build ()» в конце, что может гарантировать, что частично определенный объект не будет создан, поскольку эта операция может быть предоставлена ​​менее публичной видимости. Альтернативой будет «объект параметров», но IMHO просто подталкивает проблему на один уровень.

Мне не нравятся многопараметрические конструкторы, потому что они делают более вероятным, что передаются много аргументов того же типа, что облегчает передачу неправильных аргументов параметрам. Мне не нравится использование множества сеттеров, потому что объект может быть использован до его полной настройки. Кроме того, представление о значениях по умолчанию, основанных на предыдущих вариантах, лучше обслуживается методом «build ()».

Короче говоря, я считаю, что это хорошая практика, если использовать ее правильно.

Плохая привычка: сеттер установил getter get

что о явном объявлении метода, который делает это для U

 setPropertyFromParams(array $hashParamList) { ... } 
  • Когда мне нужно использовать интерфейсы вместо абстрактных classов?
  • Как быстро определить, переопределен ли метод в Java
  • Синглтон с аргументами в Java
  • Сцепление, сплоченность и закон Деметры
  • «Тени» против «Переопределения» в VB.NET
  • В чем разница между переопределением и новыми ключевыми словами в C #?
  • адаптер - Любой реальный пример шаблона адаптера
  • Принцип разделения сечений - программа для интерфейса
  • Почему открытые поля быстрее, чем свойства?
  • Какое определение «интерфейс» в объектно-ориентированном программировании
  • Когда нужно реализовать интерфейс и когда расширять суперclass?
  • Давайте будем гением компьютера.