让Java中的设置器返回"这个"是好还是坏的想法?
1 2 3 4
| public Employee setName (String name ){
this. name = name ;
return this;
} |
这种模式很有用,因为这样就可以设置链:
1
| list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); |
而不是这个:
1 2 3 4
| Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e); |
…但这有点违背了标准惯例。我想这可能是值得的,因为它可以使setter做一些其他有用的事情。我已经看到这个模式使用了一些地方(例如jmock、jpa),但它似乎并不常见,而且通常只用于定义很好的API,在这些API中,这个模式无处不在。
更新:
我所描述的显然是有效的,但我真正要寻找的是一些关于这是否一般可以接受的想法,以及是否存在任何陷阱或相关的最佳实践。我知道构建器模式,但它比我描述的要复杂一些——正如JoshBloch所描述的,有一个用于对象创建的关联静态构建器类。
- 因为我之前见过这种设计模式,所以我尽可能地这样做。如果一个方法不显式地需要返回一些东西来完成它的工作,那么它现在返回this。有时,我甚至修改函数,这样它就不会返回值,而是对对象的一个成员进行操作,这样我就可以做到这一点。太好了。:)
- 对于返回self的伸缩设置器和在builders中,我更喜欢使用withname(字符串名称)而不是setname(字符串名称)。正如您所指出的,对setter的一个常见做法和期望是返回void。"非标准的"setter"在现有框架(如JPA实体管理器、Spring等)中可能表现不好。
- 请在每次调用之前引入换行符:)并配置您的IDE,或者如果它不遵守这一点,则获取一个合适的IDE。
- 广泛使用的框架(例如Spring和Hibernate)将严格(至少他们曾经)遵守void setters约定。
- 另请参见stackoverflow.com/questions/5741369/…
这不是坏做法。这是一种越来越普遍的做法。如果不想,大多数语言都不要求您处理返回的对象,因此它不会更改"正常"的setter用法语法,但允许您将setter链接在一起。
这通常被称为构建器模式或流畅的接口。
在Java API中也是常见的:
1 2
| String s = new StringBuilder (). append("testing"). append(1)
. append(" 2"). append(3). toString(); |
- 它经常用于建筑工人,但我不会说"这是…称为构建器模式"。
- 我觉得有趣的是,流畅接口的一些基本原理是使代码更容易阅读。我觉得写起来更方便,但我觉得读起来更难。这是我对它唯一真正的分歧。
- 可能fluent接口的最终体现是linq
- 它也被称为火车失事反模式。问题是,当一个空指针异常堆栈跟踪包含这样一行时,您不知道哪个调用返回了空值。这并不是说应该不惜一切代价避免链接,而是要小心糟糕的图书馆(特别是家庭酿酒)。
- @建筑者的模式,尽管它和"火车残骸"有着相同的外观,但并没有违反德米特定律。这真的是一次火车失事吗?
- @韦恩·康拉德(WayneConrad)——德米特定律在这里是无关紧要的(我们不是在谈论中间人)——如果它抛出了NPE,你就不知道是哪种说法导致了它,就这样。有很多很好的例子是链接API(Easymock/Mockito想到了),但是我也看到了一些非常可怕的令人憎恶的事情,它们是调试和支持的痛苦。
- @Ddimitrov只要限制它返回这个值,就永远不会有问题(只有第一个调用可以抛出NPE)
- @Brentnash我认为真正的理由并不是它使代码更容易阅读,而是它使代码更容易编写,作者更喜欢它,并含蓄地认为它更容易阅读。
- 如果你把换行符和缩进放在可读性会受到影响的地方,那么写和读就更容易了!(因为它避免了重复代码(如bla.foo.setBar1(...) ; bla.foo.setBar2(...))的冗余混乱,当您可以编写bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)时(不能在这样的注释中使用换行符):-(…希望考虑到10个这样的setter或更复杂的调用,您能理解这一点)
- @AndreasDietrich还可以使用调试器单步执行"train":-d
- "易读"取决于人。这就是为什么我们有这么多不同的模式;)
总结:
- 它被称为"fluent接口",或"方法链接"。
- 这不是"标准"Java,虽然您现在看到的更多了(JQuery中的工作非常出色)
- 它违反了JavaBean规范,因此它将使用各种工具和库,特别是JSP构建器和Spring。
- 它可能会阻止一些JVM通常会做的优化
- 有些人认为它会清除代码,另一些人则认为它是"可怕的"
还有几点没提到:
这违背了原则,即每个函数都应该做一件(并且只有一件)事情。你可能相信,也可能不相信这一点,但在Java中,我相信它工作得很好。
IDE不会为您生成这些(默认情况下)。
最后,这里是一个真实的数据点。我在使用像这样的图书馆时遇到了问题。Hibernate的查询生成器就是现有库中的一个例子。由于查询的set*方法正在返回查询,因此不可能仅仅通过查看签名就知道如何使用它。例如:
1
| Query setWhatever (String what ); |
它引入了一种歧义:方法是否修改了当前对象(您的模式),或者查询是否真的是不可变的(一种非常流行和有价值的模式),并且该方法正在返回一个新的模式。它只是使库更难使用,而且许多程序员不利用这个特性。如果设置者是设置者,那么就更清楚如何使用它了。
- 顺便说一句,它是"流畅的",而不是"流畅的"…因为它让你像口语一样构造一系列方法调用。
- 关于不变性的最后一点非常重要。最简单的例子是字符串。Java DEVS预期,当在字符串上使用方法时,它们会得到完全新的实例,而不是相同的但修改的实例。对于Fluent接口,必须在方法文档中提到,返回的对象是"this",而不是新实例。
- 这必须被接受,而不是高于答案…
- 尽管我总体上同意,但我不同意这违反了"只做一件事"原则。返回this并不是一门复杂的火箭科学。-)
- 附加要点:它还违反了命令-查询分离原则。
我更喜欢使用"with"方法:
1 2 3 4 5 6
| public String getFoo () { return foo ; }
public void setFoo (String foo ) { this. foo = foo ; }
public Employee withFoo (String foo ) {
setFoo (foo );
return this;
} |
因此:
1 2 3
| list.add(new Employee().withName("Jack Sparrow")
.withId(1)
.withFoo("bacon!")); |
- +1代表有趣的会议。我不打算在我自己的代码中采用它,因为现在似乎每个类字段都必须有一个get、一个set和一个with。不过,这仍然是一个有趣的解决方案。:)
- 这取决于你多久打一次二传。我发现,如果这些setter经常被调用,那么添加它们就值得额外的麻烦了,因为它简化了其他地方的代码。牛传染性胃肠炎病毒
- 如果你在Project Lombok的@Getter/@Setter注释中加上这个…那对链子来说太棒了。或者您可以使用jquery和javascript定义使用的Kestrel组合器(github.com/raganwald/katy)。
- @定性-我认为代码有错误。withfoo(…)应将雇员声明为返回值-而不是字符串。
- 另外,您能指出一些使用这种模式的流行库吗?
- @谢谢,我更正了回信类型。关于你的问题,我不知道有什么流行的图书馆使用这种模式。
- 如果Java bean是一个实体呢?不能省略set和get前缀,因为它们是命名约定。
- 同意。请重新阅读我的评论,我所主张的是除了get和set方法之外,还包含一个返回self类型的with方法。
- 酷的基本理念。我在使用流畅的方法进行GWT Autobeans(de)序列化时遇到了一些问题,将来基本上会用这种方式使用你的想法(与通常的方法更接近,也更直观/更自动完成等):void setFoo(Foo foo); Employee setFoo$(Foo foo)(with)对我来说似乎不是一个更好的con论点的想法。
- 实际上,AlikElzin Kikaa我注意到Java 8中的JavaTimes不可变类使用这个模式,例如LoalDATA.WORE,WITH,等等。
- with前缀是另一种约定。正如@qualifial给出的例子。前缀为with的方法不应返回this,而是返回一个新实例,如当前实例,但with会发生更改。当您希望对象不可变时,可以这样做。所以当我看到一个以with为前缀的方法时,我想我会得到一个新的对象,而不是同一个对象。
我不认为它有什么特别的问题,只是风格问题。当:
- 您需要同时设置多个字段(包括在施工时)
- 您知道在编写代码时需要设置哪些字段,以及
- 要设置的字段有许多不同的组合。
此方法的替代方法可能是:
一个大型构造函数(缺点:您可能传递大量的空值或默认值,很难知道哪个值对应于什么)
几个重载的构造函数(缺点:一旦拥有多个构造函数,就会变得笨拙)
工厂/静态方法(缺点:与重载构造函数相同-一旦有多个构造函数就会变得笨拙)
如果一次只设置几个属性,我会说不值得返回"this"。如果您以后决定返回其他内容,比如状态/成功指示器/消息,那么它肯定会下降。
- 好吧,一般来说,按照惯例,你不会从设置者那里返回任何东西。
- 也许不是从一开始,但二传手不一定要保持它原来的目的。曾经是一个变量的东西可能会变成一个包含多个变量的状态,或者有其他副作用。一些setter可能返回一个以前的值,而另一些setter可能返回一个失败指示器,如果失败对于异常来说太常见。不过,这又引出了另一个有趣的观点:如果正在使用的工具/框架在设置器具有返回值时无法识别它们呢?
- 汤姆好点,这样做打破了"爪哇豆"公约的吸气剂和设定者。
- ToCcLoad是否打破了"Java bean"公约引起任何问题?是使用"JavaBean"约定查看返回类型还是仅使用方法参数和方法名称的库。
- 不幸的是,我正在以数据bean为中心(dtos,daos)的环境中工作,现在我无法从autobean(code.google.com/p/google web toolkit/wiki/autobean quicksta‌&8203;rt)功能中获益,这些功能无法直接处理这种非bean标准。因此,我担心会失去这里更少样板代码的优势:(。但也许有办法…我来看看。
- 这就是构建器模式存在的原因,setter不应该返回某些内容,而是在需要的时候创建一个构建器(如果它看起来更好,代码占用更少)。
- 您可以使用特殊的setter,它可以获取属性(名称和值)的映射。然后,在方法内部,可以使用反射来调用适当的setter。当您有一些重载的setter时,这种方法可能不起作用,编译时检查的好处也会丢失。
- 对于我来说,如果返回任何内容,函数的名称似乎就是谎言。如果它与对象一起返回,那么它的名称应该是setPropertyAndGetObject();set有一件事,即设置一个属性。当然,我不需要知道setter是如何实现这个集合的,例如$A->setFullName($firstName, $lastName);可以做一些逻辑,例如,在不同的国家,姓氏是第一名,名字是第二名。但它只做一件事:设置全名。
- @血管123我感觉到你的痛苦…但是如果另一个选择是有一个巨大的建造者,我可以忍受它…不管怎样,你的例子对Mee来说并不那么清楚:当你、我或任何人听到"setter"这个词时,都会想到"getter和setter",而不是"计算创建并设置值的正确策略":你的例子应该是:setfullname(calcfullname(firstname,lastname))。对于我来说,让setter在我的头脑中有一个清晰的定义,问题应该是:在setter中返回"this"这个事实会以一种戏剧性的方式改变这个定义吗?
- 返回this是Builder模式的基础。但是,返回this也是一种状态操作。没有一种尺寸适合所有人。赞成不变。构建器(通常)的关键是使构建一个不可变的对象失效保护
如果不想从setter返回'this',但不想使用第二个选项,可以使用以下语法设置属性:
1 2 3 4 5 6
| list.add(new Employee()
{{
setName("Jack Sparrow");
setId(1);
setFoo("bacon!");
}}); |
作为旁白,我认为它在C中稍微干净一点:
1 2 3 4 5
| list. Add(new Employee () {
Name ="Jack Sparrow",
Id = 1,
Foo ="bacon!"
}); |
- 双括号初始化可能会遇到equals的问题,因为它创建了一个匿名的内部类;参见c2.com/cgi/wiki?双数据库初始化
- @很明显,问题出在搞砸了equals方法的人身上。如果您知道自己在做什么,那么有非常干净的方法来处理equals中的匿名类。
- 这是我现在最喜欢的,谢谢!:)
- 但只适用于新对象!
它不仅打破了GETS/SETTER的约定,也打破了Java 8方法参考框架。MyClass::setMyValue是BiConsumer,myInstance::setMyValue是Consumer。如果您的setter返回this,那么它不再是Consumer的有效实例,而是Function,并且会导致使用这些setter的方法引用(假定它们是无效方法)的任何内容中断。
- 如果Java有某种方式通过返回类型重载,而不仅仅是JVM,那就太棒了。您可以轻松地绕过许多这些中断的更改。
- 通过提供void accept(A a) { apply(a); }的默认实现,您始终可以定义扩展Consumer和Function的功能接口。然后,它可以很容易地用作任何一个,并且不会破坏任何需要特定形式的代码。
我不知道Java,但我已经用C++完成了。有人说这句话很长很难读,但我已经这样做了很多次了:
1 2 3 4
| list.add(new Employee()
.setName("Jack Sparrow")
.setId(1)
.setFoo("bacon!")); |
这甚至更好:
1 2 3 4
| list.add(
new Employee("Jack Sparrow")
.Id(1)
.foo("bacon!")); |
至少,我认为。但如果你愿意的话,欢迎你投我一票,叫我糟糕的程序员。我不知道你是否可以用Java做这个。
- "更好的"并不能很好地利用现代IDE中可用的格式源代码功能。
- 你可能是对的…我使用的唯一自动格式化程序是Emacs的自动缩进。
- 在链中的每个方法调用之后,可以使用简单的//强制源代码格式化程序。它稍微增加了您的代码,但没有水平地重新格式化您的垂直语句系列那么多。
- "QualIDAFIAL"在每个方法之后都不需要EDCOX1×5,如果您配置IDE不加入已经打包的行(例如Eclipse >属性> Java>代码样式>格式化程序>行包装>不要加入已经包好的行)。
因为它不返回void,所以它不再是有效的JavaBean属性设置器。如果您是世界上使用可视化"bean builder"工具的七个人之一,或者是使用jsp bean setproperty元素的17个人之一,那么这可能很重要。
- 如果您使用像Spring这样的Bean感知框架,这也很重要。
- 或者,如果您使用内省API编写反射代码…
这个方案(双关语)被称为"流畅的界面",现在正变得非常流行。这是可以接受的,但这不是我真正的一杯茶。
至少在理论上,它可以通过设置调用之间的错误依赖性来破坏JVM的优化机制。
它应该是语法糖,但实际上可以在超级智能Java 43的虚拟机中产生副作用。
这就是为什么我投反对票,不要用它。
- 有意思……你能再详细讲一下吗?
- 同意,我想进一步了解这会如何影响优化
- 想想超级标量处理器如何处理并行执行。执行第二个set方法的对象依赖于第一个set方法,尽管程序员知道它。
- 我还是不明白。如果设置foo,然后使用两个单独的语句设置bar,则为其设置bar的对象的状态与为其设置foo的对象的状态不同。所以编译器也不能并行这些语句。至少,我不知道它如何能不引入一个不必要的假设。(因为我不知道它,我不会否认Java 43实际上在一次的情况下做并行化,而不是在另一种情况下引入并行,并且在一种情况下引入了不必要的假设,而不是另一种情况。)
- @马森克:也许是这样的:obj.modifyList(aList).modifyList(anotherList),其中modifyList不修改this,但需要知道this是什么。
- 如果你不知道,测试一下。-XX:+UnlockDiagnosticVMOptions -XX:+PrintInliningJava7JDK绝对是内联链接方法,并且执行相同的迭代次数,将void setter标记为hot,并将其内联。你低估了JVM的操作码修剪算法的能力;如果它知道你正在返回,它会跳过JRS(Java返回语句)操作码,然后把它放在堆栈上。
- 但是,我们真的应该考虑优化是否是他的代码的一个问题。在我的职业生涯中,我写的几乎所有代码都不是超高速运行的,而是应该是可以理解和无故障的。因此,我认为大多数代码/编码人员不需要非常关注速度,而需要关注可维护性,这也包括可读性。Java本身就是一个很好的例子,它是最慢的语言之一。几年前,但它仍然被广泛采用,因为速度会提高。不管怎样,在硬件和软件方面,可维护性更为重要。
- Andreas,我同意,但是当您有一层又一层的低效代码时,问题就会出现。99%的时间你应该为清晰而编码,这在这里被说得很多。但也有一些时候,你需要现实,并利用你多年的经验,在一般的建筑意义上过早地优化。
这一点也不坏。但它与JavaBeans规范不兼容。
有很多规范依赖于这些标准的访问器。
你可以让它们彼此共存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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 ;
} |
现在我们可以一起使用它们了。
1
| new Some().value("some").getValue(); |
下面是不可变对象的另一个版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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 ;
} |
现在我们可以这样做了。
1
| new Some.Builder().value("value").build().getValue(); |
- 我的编辑被拒绝,但您的生成器示例不正确。首先,.value()不返回任何内容,甚至不设置some字段。其次,您应该添加一个安全措施,并在build()中将some设置为null,这样some才是真正不变的,否则您可以在同一个生成器实例上再次调用builder.value()。最后,是的,你有一个建设者,但是你的some仍然有一个公共建设者,这意味着你不公开主张使用建设者,即用户除了尝试或寻找一种方法来设置自定义value之外,根本不知道它。
- @如果答案不正确,你应该写自己的答案,而不是试图编辑别人的形状。
- @ CalvT?所以我应该在需要编辑的时候留下错误的信息?这不是"编辑成形",而是纠正他犯的小错误。另外,他的建设者的例子是对一个正常答案的扩展,我不保证完全有自己的答案。
- @我刚刚更新了我的答案。
- @金昆很好。谢谢!如果我以前看起来粗鲁的话,我很抱歉。
- @Adowraith对于增强功能的任何其他评论,请放心。仅供参考,我不是那个拒绝你编辑的人。:)
- 我知道,我知道。^^感谢新版本,它现在提供了一个真正可变的"不可变的"构建器。这是一个比我的编辑尝试更聪明的解决方案,相比之下,它把代码弄乱了。
我赞成二传手有"这个"回报。我不在乎它是否不符合beans。对我来说,如果可以使用"="表达式/语句,那么返回值的setter就可以了。
PauloAbrantes提供了另一种让JavaBean setter流畅的方法:为每个JavaBean定义一个内部生成器类。如果您使用的工具被返回值的setter所迷惑,那么Paulo的模式可能会有所帮助。
- 我赞成,但你的联系断了。
- @CDUNN2001,使用回程机,卢克
我以前喜欢这种方法,但我已经决定反对它。
原因:
- 可读性。将每个setfoo()放在一个单独的行上会使代码更可读。通常,你读代码的次数比你一次写代码的次数多很多。
- 副作用:set foo()只应设置字段foo,而不应设置其他值。返回这是一个额外的"那是什么"。
我看到的构建器模式不使用setfoo(foo).setbar(bar)约定,而是使用更多的foo(foo).bar(bar)。也许正是因为这些原因。
这是,一如既往的品味问题。我只是喜欢"最不意外"的方法。
- 我同意副作用。返回内容的设置器违反了它们的名称。你在设置foo,但你得到了一个物体?这是新的东西还是我把旧的改了?
这种特殊模式称为方法链接。维基百科链接,这有更多的解释和例子说明它是如何在各种编程语言中完成的。
P.S:我只是想把它留在这里,因为我在找具体的名字。
如果你在整体上使用相同的惯例,这似乎是好的。
另一方面,如果应用程序的现有部分使用标准约定,我将坚持使用它,并将构建器添加到更复杂的类中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| 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;
}
} |
- 这正是构建器模式要修复的问题。它不破坏Java 8中的lambdas,它不会破坏精细的JavaBeans工具,并且不会在JVM中引起任何优化问题(因为对象只在实例化期间存在)。它还解决了不使用构建器以及从双括号匿名类中消除堆污染所带来的"过多构建器"问题。
是的,我觉得这是个好主意。
如果我可以添加一些内容,那么这个问题呢:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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;
}
} |
这将起作用:
1
| new Friend().setNickName("Bart").setName("Barthelemy"); |
这不会被Eclipse接受!:
1
| new Friend().setName("Barthelemy").setNickName("Bart"); |
这是因为setname()返回的是人而不是朋友,并且没有peoplesetnickname。
我们如何编写setters来返回self类而不是类的名称?
类似这样的事情会很好(如果存在self关键字的话)。这到底存在吗?
1 2 3 4 5 6 7 8 9
| class People
{
private String name ;
public SELF setName (String name )
{
this. name = name ;
return this;
}
} |
- 除了Eclipse之外,还有一些其他Java编译器不接受:基本上,你正在运行Java类型系统的FuWL(它是静态的,不像一些脚本语言那样动态):你必须在你可以通过它设置一个朋友之前将StnNAME()的内容投射到一个朋友上。因此,不幸的是,对于继承层次结构,这消除了许多可读性优势,并使这种可能有用的技术不那么有用。
- 使用泛型。class chaineablepublic self dosomething()return(self)this;it's not technically type safe(it will class cast if you implement the class with a self type you cannot be case to),but it is correct grammary,and subclasses will return their type.
- 另外:Baptiste使用的这个"self"被称为self类型,在很多语言中都不存在(scala实际上是我现在唯一能想到的一个),在Ajax使用泛型时,它是所谓的"奇怪的循环模板模式",它试图解决缺少self类型的问题,但它有一些缺点:(来自class C
>,W它比普通的S extends C更安全。a)如果A extends B和B extends C,并且您有a,那么您只知道返回a b,除非a重写每个方法。b)不能表示本地非原始C>>>>。
一般来说,这是一个很好的实践,但是您可能需要set-type函数使用boolean类型来确定操作是否成功完成,这也是一种方法。一般来说,没有什么教条可以说这是好的还是床上的,这当然是来自于当时的情况。
- 用异常来表示错误情况怎么样?错误代码很容易被忽略,正如许多C程序员痛苦地学习的那样。异常会在堆栈中冒泡到可以处理它们的位置。
- 异常通常是可取的,但当您不能使用异常时,错误代码也很有用。
- 您好@narek,也许您可以详细说明在哪些情况下,最好在setter中使用错误代码,而不是异常,以及为什么?
乍一看:"可怕!".
关于进一步的思考
1
| list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); |
实际上比
1 2 3 4
| Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee); |
非常有趣。正在向工具包添加想法…
- 不,还是很可怕。从维护的角度来看,后者更好,因为它更容易阅读。此外,像checkstyle这样的自动代码检查程序默认将行强制为80个字符-代码无论如何都将被包装,这就加剧了可读性/维护问题。最后,它是Java;当它被编译成字节代码时,在单行上写任何东西都没有好处。
- 就我个人而言,我认为前者更容易阅读,特别是如果你用这种方式创建了几个对象。
- @肯:编码一个方法。用流利的格式写一份,用另一份。现在把这两本交给几个人,问他们觉得哪一本更容易读。读取速度更快,编码速度更快。
- 像大多数工具一样,它很容易被过度使用。jQuery是围绕这种技术而开发的,因此很容易出现长的调用链,我发现这实际上会降低可读性。
- 如果在每个点之前都有换行符和缩进,那就好了。然后,它将和第二个版本一样可读。实际上,因为一开始没有多余的list。
我同意所有声称这违反了JavaBeans规范的海报。有理由保留这一点,但我也认为使用这种构建器模式(这是暗指)是有其用武之地的;只要它不在任何地方使用,它应该是可以接受的。"在我看来,这是一个地方,它的终点是对"build()"方法的调用。
当然,还有其他设置所有这些内容的方法,但是这里的优势在于它避免了1)许多参数公共构造函数和2)部分指定的对象。这里,让构建器收集所需的内容,然后在末尾调用它的"build()",这样可以确保不构建部分指定的对象,因为该操作的可见性可以低于公共可见性。另一种选择是"参数对象",但imho只是将问题向后推一级。
我不喜欢许多参数构造函数,因为它们使传入许多相同类型参数的可能性更大,这可以使向参数传递错误参数更容易。我不喜欢使用很多setter,因为对象可以在完全配置之前使用。此外,基于前面的选择拥有默认值的概念更适合使用"build()"方法。
简而言之,如果使用得当,我认为这是一个很好的做法。
我制作setter已经有一段时间了,唯一真正的问题是库中坚持使用严格的getPropertyDescriptor来获取bean读写器bean访问器。在这些情况下,您的Java"bean"将不具有您所期望的写入器。
例如,我还没有对它进行过测试,但我不会惊讶于当杰克逊从JSON/MAP创建Java对象时,不会识别这些设置器。我希望我在这个问题上是错的(我很快就会测试它)。
事实上,我正在开发一个轻量级的以SQL为中心的ORM,并且我必须在返回这个消息的公认的setter中添加一些超越getpropertydescriptor的代码。
从声明中
1
| list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); |
我看到两件事
1)无意义陈述。2)可读性不足。
很久以前的答案,但我的两分钱…很好。我希望这个流畅的界面能经常使用。
重复"factory"变量不会在下面添加更多信息:
1 2 3
| ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ... |
这更干净,imho:
1 2 3
| ProxyFactory factory = new ProxyFactory ()
. setSuperclass(Properties. class);
. setFilter(new MethodFilter () { ... |
当然,作为已经提到的答案之一,Java API必须在某些情况下进行调整,例如继承和工具。
如果可用,最好使用其他语言结构。例如,在Kotlin中,您将使用with、apply或let。如果使用这种方法,就不需要从setter返回实例。
这种方法允许您的客户机代码是:
下面是一些例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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,我使用"返回这个"来设置只设置一次的值。如果我有任何其他用户应该能够更改的值,我将使用标准的void setter。
然而,这真的是一个偏好的问题,在我看来,链接设置器看起来确实很酷。
这可能不太可读
1
| list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); |
或者这个
1 2 3 4
| list.add(new Employee()
.setName("Jack Sparrow")
.setId(1)
.setFoo("bacon!")); |
这比:
1 2 3 4 5
| Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!"));
list.add(employee); |
- 我认为如果你不把所有的代码放在一行上,它是非常可读的。
坏习惯:设置器吸气器
那么显式声明一个方法呢,这样做是为了u
1
| setPropertyFromParams(array $hashParamList) { ... } |
- 不适合自动完成和重构以及可读性和样板代码
- 因为:1)您必须记住顺序或从文档中读取顺序,2)您失去了所有编译时类型的安全性,3)您必须强制转换值(当然,这和2)在动态语言中是没有问题的),4)除了在每次调用时检查数组长度之外,您不能有效地扩展它,5)如果您更改了任何内容,就按顺序执行。甚至某些值的存在,都不能毫无疑问地检查运行时和编译时间。设置器:1),2),3):没问题。4)添加新方法不会破坏任何内容。5)明确的错误信息。