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

249

Это хорошая или плохая идея, чтобы сеттеры в 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 для создания объектов.

Кен Лю
источник
1
Так как я видел этот шаблон дизайна некоторое время назад, я делаю это везде, где это возможно. Если метод явно не должен возвращать что-либо для выполнения своей работы, он теперь возвращается this. Иногда я даже изменяю функцию так, чтобы вместо возврата значения она работала с членом объекта, просто чтобы я мог это сделать. Это замечательно. :)
Inversus
4
Для телескопических сеттеров, возвращающих себя и в конструкторах, я предпочитаю использовать withName (имя строки) вместо setName (имя строки). Как вы указали, обычной практикой и ожиданием сеттера является возврат void. «Нестандартные» сеттеры могут плохо работать с существующими
средами,
Пожалуйста, вводите разрывы строк перед каждым вызовом :) И настройте свою IDE или получите подходящую, если она не соблюдает это.
MauganRa
Широко используются рамки (Spring и Hibernate, например) будут строго (по крайней мере , они привыкли) прилипают к мочеиспусканию сеттеров конвенции
Legna

Ответы:

83

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

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

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

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

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

Том Клифт
источник
1
ну, как правило, вы ничего не возвращаете от сеттера в любом случае, по соглашению.
Кен Лю
17
Может быть, не для начала, но сеттер не обязательно сохраняет свое первоначальное назначение. То, что раньше было переменной, может измениться в состояние, которое охватывает несколько переменных или иметь другие побочные эффекты. Некоторые установщики могут возвращать предыдущее значение, другие могут возвращать индикатор сбоя, если сбой слишком распространен для исключения. Это поднимает еще один интересный момент: что, если инструмент / среда, которую вы используете, не распознает ваши сеттеры, когда они возвращают значения?
Том Клифт
12
@ Хороший момент, выполнение этого нарушает соглашение «Java bean» для методов получения и установки.
Энди Уайт
2
@TomClift Не вызывает ли нарушение соглашения "Java Bean" какие-либо проблемы? Библиотеки, которые используют соглашение "Java Bean", смотрят на тип возвращаемого значения или только параметры метода и имя метода.
Тео Бриско
3
именно поэтому шаблон Builder существует, сеттеры не должны возвращать что-либо, вместо этого создавать конструктор, если он нужен, выглядит лучше и требует меньше кода :)
RicardoE
106

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

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

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

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();
Клетус
источник
26
Он часто используется в сборщиках, но я бы не сказал «это ... называется шаблоном строителей».
Лоуренс Гонсалвес
10
Мне смешно, что некоторые из оснований для свободных интерфейсов в том, что они делают код легче для чтения. Я мог видеть, что мне удобнее писать, но мне кажется, что читать сложнее. Это единственное настоящее несогласие с этим.
Брент пишет код
30
Это также известно как антипаттерн крушения поезда. Проблема в том, что когда трассировка стека исключений с нулевым указателем содержит строку, подобную этой, вы не представляете, какой вызов вернул null. Это не значит, что цепочки следует избегать любой ценой, но остерегайтесь плохих библиотек (особенно домашних).
ддимитров
18
@ddimitrov, пока вы ограничиваете его возвращение, это никогда не будет проблемой (только первое обращение может бросить NPE)
Stefan
4
легче писать И читать, предполагая, что вы ставите переносы и отступы там, где читаемость пострадает в противном случае! (потому что это позволяет избежать избыточного беспорядка повторяющегося кода, например, bla.foo.setBar1(...) ; bla.foo.setBar2(...)когда вы можете писать bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)(не можете использовать разрывы строк в SO-комментарии, подобном этому :-( ... надеюсь, вы получите точку зрения, учитывая 10 таких установщиков или более сложные вызовы)
Andreas Dietrich
90

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

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

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

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

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

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

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

NDP
источник
5
Кстати, это «свободно», а не «плавно» ... так как позволяет структурировать серию вызовов методов, как разговорный язык.
Кен Лю
1
Последний пункт об неизменности очень важен. Самый простой пример - String. Разработчики Java ожидают, что при использовании методов в String они получают совершенно новый экземпляр, а не тот же, но измененный экземпляр. При свободном интерфейсе в документации метода должно быть упомянуто, что возвращаемый объект - это «this» вместо нового экземпляра.
MeTTeO
7
Хотя я в целом согласен, я не согласен с тем, что это нарушает принцип «делай только одно». Возвращение thisвряд ли сложная ракетостроение. :-)
user949300
дополнительный момент: это также нарушает принцип разделения команд и запросов.
Marton_hun
84

Я предпочитаю использовать методы 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!"));

Предупреждение : этот withXсинтаксис обычно используется для предоставления «установщиков» для неизменяемых объектов, поэтому вызывающие эти методы могут разумно ожидать, что они создадут новые объекты, а не изменят существующий экземпляр. Может быть, более разумная формулировка будет что-то вроде:

list.add(new Employee().chainsetName("Jack Sparrow")
                       .chainsetId(1)
                       .chainsetFoo("bacon!"));

С соглашением об именовании chainsetXyz () практически все должны быть счастливы.

qualidafial
источник
18
+1 За интересное соглашение. Я не собираюсь применять его в своем собственном коде, так как кажется, что теперь вам нужно иметь a get, a setи a withдля каждого поля класса. Это все еще интересное решение, хотя. :)
Пол Манта
1
Это зависит от того, как часто вы вызываете сеттеров. Я обнаружил, что если эти сеттеры часто вызывают, их стоит добавить, так как это упрощает код везде. YMMV
квалификационный матч
2
И если вы добавили это Project Lombok«s @Getter/@Setterаннотаций ... что бы фантастическим для построения цепочки. Или вы можете использовать что-то похожее на Kestrelкомбинатор ( github.com/raganwald/Katy ), который используют изверги JQuery и Javascript.
Этеш Чоудхури
4
@ AlikElzin-kilaka На самом деле я только что заметил, что неизменяемые классы java.time в Java 8 используют этот шаблон, например LocalDate.withMonth, withYear и т. Д.
qualidafial
4
withпрефикс другой конвенции. как @qualidafial привел пример. методы с префиксом withдолжны возвращать не thisновый экземпляр, например текущий, но withэто изменение. Это делается, когда вы хотите, чтобы ваши объекты были неизменными. Поэтому, когда я вижу метод с префиксом, withя предполагаю, что получу новый объект, а не тот же объект.
темп
26

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

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

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

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});
Люк Куинан
источник
16
инициализация двойной скобки может иметь проблемы с equals, потому что она создает анонимный внутренний класс; см. c2.com/cgi/wiki?DoubleBraceInitialization
Csaba_H
@ Csaba_H Ясно, что эта проблема - ошибка человека, который испортил equalsметод. Существуют очень простые способы работы с анонимными классами, equalsесли вы знаете, что делаете.
AJMansfield
создать новый (анонимный) класс только для этого? для каждого случая?
user85421
11

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

Стив К
источник
2
Было бы здорово, если бы у Java был какой-то способ перегрузки по типу возвращаемого значения, а не только по JVM. Вы можете легко обойти многие из этих критических изменений.
Adowrath
1
Вы всегда можете определить функциональный интерфейс, который расширяет оба Consumer<A>и Function<A,B>предоставляя реализацию по умолчанию void accept(A a) { apply(a); }. Тогда он может легко использоваться как один из них и не будет нарушать любой код, который требует определенной формы.
Стив К
Argh! Это просто неправильно! Это называется void-совместимостью. Сеттер, который возвращает значение, может действовать как потребитель. ideone.com/ZIDy2M
Майкл
8

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

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

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

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

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

Карсон Майерс
источник
«Еще лучше» плохо подходит для функциональности Format Source code, доступной в современных IDE. К сожалению.
Торбьерн Равн Андерсен
Вы, вероятно, правы ... Единственный автоформатер, который я использовал, - это автоматический отступ Emacs.
Карсон Майерс
2
Форматировщики исходного кода можно принудительно вводить с помощью простого // после каждого вызова метода в цепочке. Это немного портит ваш код, но не так сильно, потому что ваш вертикальный ряд операторов переформатирован горизонтально.
qualidafial
@qualidafial Вам не нужно //после каждого метода, если вы настраиваете свою IDE, не присоединять уже обернутые строки (например, Eclipse> Свойства> Java> Стиль кода> Форматер> Обтекание строк> Никогда не объединять обернутые строки).
DJDaveMark
6

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

Шелковый полдень
источник
6

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

кругозор
источник
Это также имеет значение, если вы используете бин-ориентированные фреймворки, такие как Spring.
ддимитров
6

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

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

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

Мэриан
источник
10
Интересно ... не могли бы вы рассказать об этом немного?
Кен Лю
3
Подумайте, как суперскалярные процессоры справляются с параллельным выполнением. Объект для выполнения второго setметода зависит от первого setметода, хотя он известен программисту.
Marian
2
Я до сих пор не следую. Если вы устанавливаете Foo, а затем Bar с двумя отдельными операторами, объект, для которого вы устанавливаете Bar, имеет состояние, отличное от объекта, для которого вы устанавливаете Foo. Таким образом, компилятор также не может распараллелить эти операторы. По крайней мере, я не понимаю, как это могло бы произойти без введения необоснованного предположения. (Поскольку я понятия не имею об этом, я не буду отрицать, что Java 43 фактически выполняет распараллеливание в одном случае, но не в другом, и вводит необоснованное предположение в одном случае, но не в другом).
масон
12
Если ты не знаешь, проверь. -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Java7 jdk однозначно встраивает цепочечные методы и делает примерно такое же количество итераций, которое требуется, чтобы пометить пустые сеттеры как горячие и встроить их тоже. Считаете, что вы недооцениваете силу алгоритмов сокращения кода операции JVM; если он знает, что вы возвращаете это, он пропустит код операции jrs (оператор возврата java) и просто оставит его в стеке.
Ajax
1
Андреас, я согласен, но проблемы возникают, когда у вас слой за слоем неэффективного кода. 99% времени вы должны писать для ясности, о чем здесь много говорят. Но бывают случаи, когда вам нужно быть реалистами и использовать свой многолетний опыт, чтобы преждевременно оптимизировать в общем архитектурном смысле.
LegendLength
6

Это совсем не плохая практика. Но это не совместимо с 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();
Джин Квон
источник
2
Мое редактирование отклонено, но ваш пример Builder неверен. Во-первых, .value () ничего не возвращает и даже не устанавливает someполе. Во- вторых, вы должны добавить гарантию и набор someдля nullтелосложением () , так что Someэто действительно неизменны, в противном случае вы могли бы назвать builder.value()на том же экземпляре Builder снова. И, наконец, да, у вас есть конструктор, но у вас Someвсе еще есть публичный конструктор, что означает, что вы не пропагандируете использование конструктора открыто, т. Е. Пользователь не знает об этом, кроме как путем попытки или поиска метода для установки пользовательского valueвообще.
Adowrath
@ Adowrath, если ответ неправильный, вы должны написать свой собственный ответ, а не пытаться изменить кого-то в форму
CalvT
1
@JinKwon Отлично. Спасибо! И извините, если я казался грубым раньше.
Adowrath
1
@ Adowrath Пожалуйста, не стесняйтесь любых дополнительных комментариев для улучшений. К вашему сведению, я не тот, кто отклонил ваши изменения. :)
Джин Квон
1
Я знаю я знаю. ^^ И спасибо за новую версию, которая теперь предоставляет действительно изменяемый конструктор "Immutable-Some". И это более умное решение, чем мои попытки редактирования, которые, по сравнению, загромождали код.
Adowrath
4

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

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

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;
    }
}
Марчин Шимчак
источник
1
Это именно то, что шаблон Builder был разработан, чтобы исправить. Он не ломает лямбды в Java 8, не ломает привередливые инструменты JavaBeans и не вызывает проблем оптимизации в JVM (поскольку объект существует только во время создания экземпляра). Это также решает проблему «слишком большого числа конструкторов», которую вы получаете, просто не используя компоновщики, а также устраняет загрязнение кучи в анонимных классах с двойными скобками.
ndm13
Интересный момент - я замечаю, что если в вашем классе почти ничего не является неизменным (например, настраиваемый графический интерфейс), то вы, вероятно, можете вообще избежать Builder.
Филипп Гин
4

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

Джим Ферранс
источник
3

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

Монти Холл
источник
2

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

Причины:

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

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

Это как всегда дело вкуса. Мне просто нравится подход "меньше всего сюрпризов".

Турбьерн Равн Андерсен
источник
2
Я согласен на побочный эффект. Сеттеры, которые возвращают вещи, нарушают их имя. Вы настраиваете foo, но получаете объект обратно? Это новый объект или я изменил старый?
crunchdog
2

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

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

capt.swag
источник
1

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

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

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

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

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

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

DJNA
источник
1
Нет, это все еще ужасно. С точки зрения обслуживания, последний лучше, потому что его легче читать. Кроме того, автоматические средства проверки кода, такие как CheckStyle, принудительно заставят строки по 80 символов по умолчанию - в любом случае код будет перенесен, что усугубит проблему читабельности / сопровождения. И наконец - это Java; нет никакой выгоды писать все в одной строке, если она все равно будет скомпилирована в байт-код.
OMG Ponies
1
лично я думаю, что первое легче читать, особенно если вы создаете несколько объектов таким образом.
Кен Лю
@ Кен: код метода. Написать одну копию в свободном формате; другой экземпляр в другом. Теперь дайте две копии нескольким людям и спросите, какой из них они находят более легким для чтения. Быстрее читать, быстрее кодировать.
OMG Ponies
Как и большинство инструментов, его легко использовать. JQuery ориентирован на эту технику и поэтому склонен к длинным цепочкам вызовов, которые, как я обнаружил, фактически ухудшают читабельность.
staticsan
2
Было бы хорошо, если бы перед каждой точкой и отступом были разрывы строк. Тогда это будет так же читабельно, как и вторая версия. На самом деле больше, потому что listв начале нет избыточных .
МауганРа
1

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

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

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 () возвращает People, а не Friend, и PeoplesetNickName отсутствует.

Как мы могли бы написать сеттеры, чтобы они возвращали класс SELF вместо имени класса?

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

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}
Батист
источник
1
Есть несколько других Java-компиляторов, кроме Eclipse, которые не примут это :). По сути, вы работаете с птицей системы типов Java (которая статична, а не динамична, как некоторые языки сценариев): вам придется разыграть то, что выходит из setName (), другу, прежде чем вы сможете установить на нем setNickName (). Так что, к сожалению, для иерархий наследования это устраняет большую часть преимуществ читабельности и делает эту потенциально потенциально полезную технику не очень полезной.
Корнел Массон
5
Используйте дженерики. class Chainable <Self extends Chainable> {public Self doSomething () {return (Self) this;}} Это технически не безопасно для типов (будет приведено приведение классов, если вы реализуете класс с типом Self, к которому вы не можете обращаться), но это правильная грамматика, и подклассы будут возвращать свой собственный тип.
Ajax
Кроме того: это «САМОЕ», которое использует Баптист, называется самовоспроизведением, которого нет во многих языках (Scala - действительно единственный, о котором я могу думать сейчас), где использование Generics в Ajax'е так называемое «Любопытно» шаблон повторяющегося шаблона ", который пытается справиться с отсутствием типа self, но у него есть некоторые недостатки: (из class C<S extends C<S>>, который безопаснее простого S extends C. a) Если A extends B, и B extends C<B>, и у вас есть A, вы знаете только, что B возвращается, если A не переопределяет каждый метод. б) Вы не можете обозначить местный, не необработанный C<C<C<C<C<...>>>>>.
Adowrath
1

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

Нарек
источник
2
Как насчет использования исключений для обозначения состояния ошибки? Коды ошибок могут быть легко проигнорированы, как мучительно усвоили многие программисты на Си. Исключения могут пузырить стек до точки, где они могут быть обработаны.
ддимитров
Исключения обычно предпочтительнее, но коды ошибок также полезны, когда вы не можете использовать исключения.
Нарек
1
Привет @Narek, возможно, вы могли бы уточнить, в каких случаях предпочтительнее использовать коды ошибок в установщиках, а не в исключениях, и почему?
Ддимитров
0

Из заявления

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

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

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

Мадху
источник
0

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

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); 
Скотт
источник
8
Я думаю, что это довольно читабельно, если вы не пытаетесь поместить весь код в одну строку.
Кен Лю
0

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

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

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

Джереми Чон
источник
0

Давно отвечу, но мои два цента ... Хорошо. Я бы хотел, чтобы этот свободный интерфейс использовался чаще.

Повторение переменной '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, чтобы сделать это правильно.

Josef.B
источник
0

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

Такой подход позволяет вашему клиентскому коду быть:

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

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

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"
}
Стивен Спунгин
источник
0

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

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

Кайзер кейстер
источник
0

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

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

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

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

Javaneer
источник
-4

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

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

setPropertyFromParams(array $hashParamList) { ... }
Ульрих Теви Хорус
источник
не подходит для автозаполнения и рефакторинга, а также читабельности и стандартного кода
Андреас Дитрих
Потому что: 1) вы должны помнить порядок или читать его из документов, 2) вы теряете всю безопасность типов во время компиляции, 3) вы должны приводить значения (это и 2), конечно, не проблема в динамическом язык, конечно), 4) вы не можете расширить это полезно, кроме проверки длины массива при каждом вызове, и 5) если вы что-то измените, будь то порядок или даже наличие определенных значений, вы не можете проверить, что ни время выполнения ни компилировать timewithout сомнение вообще . С сеттерами: 1), 2), 3): не проблема. 4) Добавление новых методов ничего не нарушает. 5) явное сообщение об ошибке.
Adowrath