Ken*_*Liu 235 java oop api design-patterns
在java中使setter返回"this"是一个好主意还是坏主意?
public Employee setName(String name){
this.name = name;
return this;
}
Run Code Online (Sandbox Code Playgroud)
这种模式很有用,因为你可以链接这样的setter:
list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));
Run Code Online (Sandbox Code Playgroud)
而不是这个:
Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);
Run Code Online (Sandbox Code Playgroud)
......但这有点违反标准惯例.我想这可能是值得的,因为它可以让那个setter做一些有用的东西.我已经看到这种模式使用了一些地方(例如JMock,JPA),但它似乎并不常见,并且通常仅用于定义良好的API,其中此模式随处可用.
更新:
我所描述的内容显然是有效的,但我真正想要的是关于这是否普遍可以接受,以及是否存在任何陷阱或相关最佳实践的一些想法.我知道Builder模式,但它比我描述的要多一些 - 正如Josh Bloch描述的那样,有一个关联的静态Builder类用于创建对象.
cle*_*tus 101
这不是一种糟糕的做法.这是一种越来越普遍的做法.如果您不想这样做,大多数语言都不需要您处理返回的对象,因此它不会更改"正常"的setter使用语法,但允许您将setter链接在一起.
这通常称为构建器模式或流畅的接口.
它在Java API中也很常见:
String s = new StringBuilder().append("testing ").append(1)
.append(" 2 ").append(3).toString();
Run Code Online (Sandbox Code Playgroud)
ndp*_*ndp 83
总结一下:
还没有提到其他几点:
这违反了每个函数应该做一个(并且只有一个)事情的原则.您可能会或可能不会相信这一点,但在Java中我相信它运作良好.
IDE不会为您生成这些(默认情况下).
我终于,这是一个真实的数据点.我使用像这样构建的库时遇到了问题.Hibernate的查询构建器就是现有库中的一个示例.由于Query的set*方法正在返回查询,因此仅通过查看签名如何使用它是不可能的.例如:
Query setWhatever(String what);
Run Code Online (Sandbox Code Playgroud)它引入了歧义:该方法是否修改了当前对象(您的模式),或者,Query可能是不可变的(非常流行且有价值的模式),并且该方法返回一个新方法.它只是使库更难使用,许多程序员不利用这个功能.如果setter是setter,那么如何使用它会更清楚.
qua*_*ial 77
我更喜欢使用'with'方法:
public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
setFoo(foo);
return this;
}
Run Code Online (Sandbox Code Playgroud)
从而:
list.add(new Employee().withName("Jack Sparrow")
.withId(1)
.withFoo("bacon!"));
Run Code Online (Sandbox Code Playgroud)
Tom*_*ift 75
我不认为它有什么特别的错误,这只是风格问题.它在以下情况下很有用:
此方法的替代方法可能是:
如果你只想一次设置一些房产,我会说不值得返回'这个'.如果您以后决定返回其他内容,例如状态/成功指示符/消息,它肯定会失败.
Luk*_*ane 25
如果您不想'this'
从setter 返回但不想使用第二个选项,则可以使用以下语法来设置属性:
list.add(new Employee()
{{
setName("Jack Sparrow");
setId(1);
setFoo("bacon!");
}});
Run Code Online (Sandbox Code Playgroud)
另外,我认为它在C#中略显清晰:
list.Add(new Employee() {
Name = "Jack Sparrow",
Id = 1,
Foo = "bacon!"
});
Run Code Online (Sandbox Code Playgroud)
Ste*_*e K 10
它不仅打破了getter/setter的惯例,还打破了Java 8方法参考框架.MyClass::setMyValue
是一个BiConsumer<MyClass,MyValue>
,myInstance::setMyValue
是一个Consumer<MyValue>
.如果你有你的setter return this
,那么它不再是一个有效的实例Consumer<MyValue>
,而是一个Function<MyValue,MyClass>
,并且会导致使用对这些setter的方法引用(假设它们是void方法)来破坏.
我不懂Java,但我用C++做过.其他人说它会让线条很长很难读,但我已经这么做了很多次:
list.add(new Employee()
.setName("Jack Sparrow")
.setId(1)
.setFoo("bacon!"));
Run Code Online (Sandbox Code Playgroud)
这甚至更好:
list.add(
new Employee("Jack Sparrow")
.Id(1)
.foo("bacon!"));
Run Code Online (Sandbox Code Playgroud)
至少,我想.但是如果你愿意的话,欢迎你给我一个可怕的程序员.而且我不知道你是否被允许在Java中做到这一点.
小智 6
因为它不返回void,所以它不再是有效的JavaBean属性setter.如果您是世界上使用可视化"Bean Builder"工具的七个人之一,或使用JSP-bean-setProperty元素的17个人之一,这可能很重要.
至少在理论上,它可以通过设置调用之间的错误依赖性来破坏JVM的优化机制.
它应该是语法糖,但事实上可以在超级智能Java 43的虚拟机中产生副作用.
这就是为什么我投票不,不要使用它.
这根本不是一个坏习惯.但它与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;
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以一起使用它们.
new Some().value("some").getValue();
Run Code Online (Sandbox Code Playgroud)
这是另一个不可变对象的版本.
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;
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以做到这一点.
new Some.Builder().value("value").build().getValue();
Run Code Online (Sandbox Code Playgroud)
如果您在整个应用程序中使用相同的约定,那似乎没问题。
另一方面,如果应用程序的现有部分使用标准约定,我会坚持它并将构建器添加到更复杂的类中
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;
}
}
Run Code Online (Sandbox Code Playgroud)