Это плохая практика, чтобы заставить сеттер возвращать «это»? - 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 ]

5 голосов
/ 20 апреля 2016

Это совсем не плохая практика. Но он не совместим с 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();
3 голосов
/ 01 ноября 2009

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

3 голосов
/ 02 марта 2010

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

2 голосов
/ 27 апреля 2017

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

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

2 голосов
/ 27 июня 2017

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

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

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;
    }
}
2 голосов
/ 28 августа 2009

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

Причины:

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

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

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

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

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

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

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

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

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

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

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

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

1 голос
/ 04 мая 2012

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

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

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;
    }
}
0 голосов
/ 18 августа 2018

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

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

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

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

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