让setter返回“this”是不好的做法吗?

问题

在java中使setter返回"this"是一个好主意还是坏主意?

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

这种模式很有用,因为你可以链接这样的setter:

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

......但这有点违反标准惯例。我想这可能是值得的,因为它可以让那个setter做一些有用的东西。我已经看到这种模式使用了一些地方(例如JMock,JPA),但它似乎并不常见,并且通常仅用于定义良好的API,其中此模式在任何地方都使用。

更新:

我所描述的内容显然是有效的,但我真正想要的是关于这是否普遍可以接受,以及是否存在任何陷阱或相关最佳实践的一些想法。我知道Builder模式,但它比我描述的要多一点 - 正如Josh Bloch描述的那样,有一个关联的静态Builder类用于创建对象。


#1 热门回答(91 赞)

这不是坏习惯。这是一种越来越普遍的做法。如果你不想这样做,大多数语言都不要求你处理返回的对象,因此它不会更改"正常"的setter使用语法,但允许你将setter链接在一起。

这通常称为构建器模式或afluent interface

它在Java API中也很常见:

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

#2 热门回答(69 赞)

总结一下:

  • 它被称为"流畅的界面",或"方法链"。
  • 这不是"标准"Java,虽然你现在确实看到它更多(在jQuery中运行得很好)
  • 它违反了JavaBean规范,因此它会破坏各种工具和库,特别是JSP构建器和Spring。
  • 它可能会阻止JVM通常会做的一些优化
  • 有些人认为它可以清理代码,有些人则认为它是"可怕的"

还没有提到其他几点:

  • 这违反了每个函数应该做一个(并且只有一个)事情的原则。你可能会或可能不会相信这一点,但在Java中我认为它运作良好。
  • IDE不会为你生成这些(默认情况下)。
  • 我终于,这是一个真实的数据点。我使用像这样构建的库时遇到了问题。 Hibernate的查询构建器就是现有库中的一个示例。由于Query的set *方法正在返回查询,因此仅通过查看签名如何使用它是不可能的。例如:Query setWhatever(String what);
  • 它引入了歧义:方法是否修改当前对象(你的模式),或者,Query可能是不可变的(非常流行且有价值的模式),并且该方法返回一个新方法。它只是使库更难使用,许多程序员不利用这个功能。如果setter是setter,那么如何使用它会更清楚。

#3 热门回答(69 赞)

我更喜欢使用'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!"));