java 方法链的优缺点以及用对象本身替换所有无效返回参数的可能性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16976150/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Benefits and drawbacks of method chaining and a possibility to replace all void return parameters by the object itself
提问by MartinTeeVarga
I am mostly interested in Java, but I think it's a general question. Recently I've been working with Arquillian framework (ShrinkWrap
) that uses a lot of method chaining. Other example of method chaining are methods in StringBuilder
, StringBuffer
. There are obvious benefits of using this approach: reduced verbosity is one of them.
我最感兴趣的是 Java,但我认为这是一个普遍的问题。最近我一直在使用 Arquillian 框架 ( ShrinkWrap
),它使用了很多方法链。方法链接的其他实例是在方法StringBuilder
,StringBuffer
。使用这种方法有明显的好处:减少冗长就是其中之一。
Now I was wondering, why aren't all methods which have void
return parameter implemented as chainable? There must be some obvious and objective drawback in chaining. Because if all methods are chainable, I can still choose not to use it.
现在我想知道,为什么不是所有具有void
返回参数的方法都实现为可链接的?链接肯定有一些明显和客观的缺点。因为如果所有方法都是可链接的,我仍然可以选择不使用它。
I am not asking to change the existing code in Java, which might break something somewhere, but explanation why wasn't it used would be nice as well. I am more asking from a future framework (written in Java) design perspective.
我不是要求更改 Java 中的现有代码,这可能会破坏某处的某些内容,但解释为什么不使用它也会很好。我更多地是从未来框架(用 Java 编写)设计的角度提出问题。
I have found a similar question, but the original asker is actually wondering why it IS considered a good practice: Method chaining - why is it a good practice, or not?
我发现了一个类似的问题,但最初的提问者实际上想知道为什么它被认为是一个好习惯:方法链 - 为什么它是一个好习惯,或者不是?
While there are some answers available, I am still not sure what are all the benefits and drawbacks of chaining and whether it would be considered useful to have all void methods chainable.
虽然有一些可用的答案,但我仍然不确定链接的所有优点和缺点是什么,以及将所有无效方法链接起来是否有用。
回答by Richard Tingle
Drawbacks
缺点
- Principally it confuses the signature, if something returns a new instance I don't expect it to also be a mutator method. For example if a vector has a scale method then if it has a return I would presume it returns a new vector scaled by the input, if it doesn't then I would expect it to scale internally.
- Plus of course you get problems if the class is extended, where partway through your chaining your object gets cast to a supertype. This occurs when a chaining method is declared in the parent class, but used on an instance of the child class.
- 主要是它混淆了签名,如果有东西返回一个新实例,我不希望它也是一个 mutator 方法。例如,如果一个向量有一个 scale 方法,那么如果它有一个返回值,我会假设它返回一个由输入缩放的新向量,如果没有,那么我希望它在内部进行缩放。
- 另外,如果类被扩展,当然你会遇到问题,在链接过程中,你的对象被强制转换为超类型。当在父类中声明链接方法但在子类的实例上使用时,就会发生这种情况。
Benefits
好处
It allows mathematical equationstyle code to be written as full equations without the need for multiple intermediate objects (leading to unnecessary overhead), for example without method chaining the vector triple cross product (as a random example) would have to be written either as
MyVector3d tripleCrossProduct=(vector1.multiply(vector2)).multiply(vector3);
which has the disadvantage of creating an intermediate object which must be created and garbage collected, or
MyVector3d tripleCrossProduct=vector1; tripleCrossProduct.multiplyLocal(vec2); tripleCrossProduct.multiplyLocal(vec3);
which avoids the creation of intermediate objects butis deeply unclear, the variable name
tripleCrossProduct
is in fact a lie until line 3. However, if you have method chaining this can be written concisely in a normal mathematical way without creating unnecessary intermediate objects.MyVector3d tripleCrossProduct=vector1.multiplyLocal(vector2).multiplyLocal(vector3);
All of this assumes that vector1 is sacrificial and will never need to be used again
And of course the obvious benefit; brevity. Even if your operations aren't linked in the manor of my above example you can still avoid unnecessary references to the object
SomeObject someObject=new SomeObject(); someObject .someOperation() .someOtherOperation();
它允许将数学方程样式的代码编写为完整的方程,而无需多个中间对象(导致不必要的开销),例如,如果没有链接向量三叉积的方法(作为随机示例),则必须将其编写为
MyVector3d tripleCrossProduct=(vector1.multiply(vector2)).multiply(vector3);
它的缺点是创建必须创建和垃圾收集的中间对象,或
MyVector3d tripleCrossProduct=vector1; tripleCrossProduct.multiplyLocal(vec2); tripleCrossProduct.multiplyLocal(vec3);
这避免了创建中间对象但非常不清楚,变量名称
tripleCrossProduct
实际上是一个谎言,直到第 3 行。然而,如果你有方法链,这可以用正常的数学方式简洁地编写,而不会创建不必要的中间对象。MyVector3d tripleCrossProduct=vector1.multiplyLocal(vector2).multiplyLocal(vector3);
所有这些都假设 vector1 是牺牲性的并且永远不需要再次使用
当然还有明显的好处;简洁。即使您的操作未在我上面示例的庄园中链接,您仍然可以避免对对象进行不必要的引用
SomeObject someObject=new SomeObject(); someObject .someOperation() .someOtherOperation();
NB MyVector3d
is not being used as a real class of Java, but is assumed to perform the cross product when .multiply()
methods are called. .cross()
is not used so that the 'intention' is clearer to those not familiar with vector calculus
NB Amit's solution was the first answer to use multiline method chaining, I include it as part of the forth bullet point for completeness
NBMyVector3d
没有被用作真正的 Java 类,而是假定.multiply()
在调用方法时执行叉积。.cross()
不使用这样的“意图”是更清楚那些不熟悉的矢量运算
NB Amit的解决方案是使用多方法链的第一个答案,我把它作为第四子弹点完整性的一部分
回答by Tengiz
Method chaining is a way to implement fluent interfaces, regardless of the programming language. Main benefit of it (readable code) tells you exactly when to use it. If there is no particular need for the readable code, better avoid using it, unless the API is naturally designed to return the context/object as a result of the method calls.
方法链是实现流畅接口的一种方式,与编程语言无关。它的主要好处(可读代码)告诉您何时使用它。如果没有特别需要可读代码,最好避免使用它,除非 API 自然地设计为作为方法调用的结果返回上下文/对象。
Step 1: Fluent Interface vs. Command-Query API
第 1 步:Fluent Interface 与 Command-Query API
Fluent interface must be considered against command-query API. To understand it better, let me write a bullet-list definition of the command-query API below. In simple words, this is just a standard object-oriented coding approach:
必须针对命令查询 API 考虑 Fluent 接口。为了更好地理解它,让我在下面编写命令查询 API 的项目符号列表定义。简单来说,这只是一种标准的面向对象编码方法:
- Method that modifies the data is called a
Command
. Command does not return a value. - Method that returns a value is called a
Query
. Query does not modify the data.
- 修改数据的方法称为 a
Command
。命令不返回值。 - 返回值的方法称为 a
Query
。查询不修改数据。
Following the command-query API will give you the benefits like:
遵循命令查询 API 将为您带来以下好处:
- Looking at the object-oriented code you understand what's happening.
- Debugging of the code is easier because each call happens separately.
- 查看面向对象的代码,您就会明白发生了什么。
- 代码的调试更容易,因为每个调用都是单独发生的。
Step 2: Fluent Interface on top of Command-Query API
第 2 步:基于命令查询 API 的流畅界面
But the command-query API exists for some reason, and it indeed, reads better. Then how do we have the benefits of both fluent interface and command-query API?
但是命令查询 API 的存在是出于某种原因,而且确实读起来更好。那么我们如何同时拥有 fluent interface 和 command-query API 的好处呢?
Answer: fluent interface must be implemented on top of the command-query API (as opposed to replacing the command-query API bythe fluent interface). Think of a fluent interface as a facade over the command-query API. And after all, it's called fluent "interface" - a readableor convenience interfaceover the standard (command-query) API.
回答:流利接口必须在命令查询API的顶部来实现(而不是更换命令查询API由流畅界面)。将流畅的界面视为命令查询 API 的外观。毕竟,它被称为流畅的“接口”——一个基于标准(命令-查询)API的可读或方便的接口。
Usually, after the command-query API is ready (written, probably unit-tested, polished to easily debug), you can write a fluent interface software layer on top of it. In other words, fluent interface accomplishes its functions by utilizing the command-query API. Then, use the fluent interface (with method chaining) wherever you want a convenience and readability. However, once you want to understand what's actually happening (e.g. when debugging an exception), you can always dig into the command-query API - good old object-oriented code.
通常,在命令查询 API 准备好(编写,可能经过单元测试,打磨以方便调试)之后,您可以在其上编写流畅的界面软件层。换句话说,fluent interface 是通过使用命令查询 API 来完成其功能的。然后,在您想要方便和可读的任何地方使用流畅的接口(带有方法链)。但是,一旦您想了解实际发生的情况(例如,在调试异常时),您总是可以深入研究命令查询 API - 很好的面向对象的旧代码。
回答by Harish Kumar
The downside I have found of using method chaining is debugging the code when NullPointerException
or any other Exception
happens. Suppose you are having following code:
我发现使用方法链的缺点是在发生NullPointerException
或Exception
发生任何其他情况时调试代码。假设您有以下代码:
String test = "TestMethodChain";
test.substring(0,10).charAt(11); //This is just an example
String test = "TestMethodChain";
test.substring(0,10).charAt(11); //This is just an example
Then you will get String index out of range: exception when executing above code. When you get into realtime situations and such things happen then you get at which line error has happened but not what part of chained method caused it. So it needs to be used judiciously where it known that data will always come or errors are handled properly.
然后你会得到字符串索引超出范围:执行上面的代码时出现异常。当您进入实时情况并且发生这样的事情时,您就会知道发生了哪一行错误,而不是链接方法的哪一部分导致了它。因此,在知道数据总会出现或错误得到正确处理的情况下,需要明智地使用它。
It has its advantages too like you need not write multiple lines of code and using multiple variables.
它也有它的优点,比如你不需要编写多行代码和使用多个变量。
Many frameworks/tools use this like Dozer and when I used to debug the code it I had to look through each part of chain to find what caused error.
许多框架/工具都像 Dozer 一样使用它,当我用来调试代码时,我必须查看链的每个部分以找出导致错误的原因。
Hope this helps.
希望这可以帮助。
回答by Nitin Tripathi
You may like to read about Fluent Interface by Martin Fowler
您可能喜欢阅读Martin Fowler 的 Fluent Interface
Summarily
概括地说
- do not chain method for the heck of it primarily because it breaks the Command Query Responsibility Segregation (CQRS) design principle.
- improve the api design by bringing it closer to the way business talks about those operations, think about them as internal DSLs
- try avoiding chaining independent methods because they pollute the API and may not be able to reveal intent to the client/maintainer of the code
- 不要将方法链接起来,主要是因为它违反了命令查询职责分离 (CQRS) 设计原则。
- 通过使其更接近业务谈论这些操作的方式来改进 api 设计,将它们视为内部 DSL
- 尽量避免链接独立的方法,因为它们会污染 API 并且可能无法向代码的客户端/维护者透露意图
回答by Amit
This pattern is useful when there is series of update needs to be done on the same object and the update operations don't need to return any status of updation. For example I used this pattern in some api I wrote for database layer. For fetching some rows based on many criteria many conditions needs to be added to where clause. Using this pattern the criteria can be added as follows.
当需要对同一对象进行一系列更新并且更新操作不需要返回任何更新状态时,此模式很有用。例如,我在为数据库层编写的一些 api 中使用了这种模式。为了根据许多条件获取一些行,需要将许多条件添加到 where 子句中。使用此模式,可以按如下方式添加标准。
CriteriaCollection().instance()
.addSelect(Criteria.equalTo(XyzCriteria.COLUMN_1, value1))
.addSelect(Criteria.equalTo(XyzCriteria.COLUMN_2, value2))
.addSelect(Criteria.isIn(XyzCriteria.COLUMN_3, values3))
.addOrder(OrderCriteria.desc(XyzCriteria.Order.COLUMN_1));
Ultimately it improves the readability of the code.
最终它提高了代码的可读性。
回答by Beryllium
If you prefer immutabilityand functional programming, you never return void
.
如果你更喜欢不变性和函数式编程,你永远不会回来void
。
A function without return value is only called for its side-effect.
一个没有返回值的函数只会因为它的副作用而被调用。
Of course there are situations where this is not applicable, but a function returning void
could be considered as a hint to try it in a different way.
当然,在某些情况下这是不适用的,但是void
可以将函数返回视为以不同方式尝试它的提示。
回答by Craig
Personally I think that it is a very useful pattern, but it should not be used everywhere. Consider the situation where you have a copyTo(T other)
method. Normally you would expect it to not return anything, but if it were to return an object of the same type, which object would you expect? This sort of issue can be cleared up with documentation, but it is still ambiguous on the method signiature.
我个人认为这是一个非常有用的模式,但不应该到处使用。考虑您有copyTo(T other)
方法的情况。通常你会期望它不返回任何东西,但如果它返回一个相同类型的对象,你会期望哪个对象?这类问题可以通过文档解决,但在方法签名上仍然不明确。
public class MyObject {
// ... my data
public MyObject copyTo(MyObject other) {
//... copy data
// what should I return?
return this;
}
}
回答by buritos
It a considerable amount of work. Especially when inheritance is involved. Have a look at this great article about the builder pattern
这是一个相当大的工作量。尤其是当涉及到继承时。看看这篇关于构建器模式的好文章
It makes a lot of sense to implement it when you expect that clients will call several methods on the same instance sequentially e.g. builder pattern, immutable objects, etc. But in my opinion in most cases it doesn't really worth the extra effort.
当您期望客户端将在同一个实例上按顺序调用多个方法(例如构建器模式、不可变对象等)时,实现它很有意义。但在我看来,在大多数情况下,这并不值得付出额外的努力。
回答by Loek Bergman
Objects have attributes and methods. Each method fulfills a part of the overall purpose of the object. Some type of methods, like constructors and getters and setters, are performing the life cycle management of the attributes and the object itself. Other methods return the state of the object and its attributes. Those methods are normally non void.
对象具有属性和方法。每种方法都实现了对象总体目的的一部分。某些类型的方法,如构造函数、getter 和 setter,正在执行属性和对象本身的生命周期管理。其他方法返回对象的状态及其属性。这些方法通常是非无效的。
Void methods come in two flavors: 1. concerning the life cycle management of the object or the attributes. 2. having an output that is completely handled within the method and should not cause any state of change anywhere else.
void 方法有两种形式: 1. 关于对象或属性的生命周期管理。2. 有一个完全在方法内处理的输出,不应在其他任何地方引起任何状态变化。
ad 1. They belong to the internal working of the object. ad 2. The information of the parameters is used to execute some work within the method. After the method has completed has there been no change in effect on the object itself nor on the internal status of one of the parameters.
ad 1.它们属于对象的内部工作。ad 2. 参数信息用于在方法内执行一些工作。方法完成后,对象本身和其中一个参数的内部状态都没有变化。
But why should you make a method returning void and why not for instance a boolean (success or not)? The motivation for a method to return void (like in a setter) is that the method is intended to have no side effect. Returning true or false might be a side effect. Void implies that the method has no side effect when the method is executed as designed. A void method returning an exception is good, because an exception is not a side effect of the normal processing of a method.
但是为什么你应该让一个方法返回 void 而为什么不例如一个布尔值(成功与否)?方法返回 void(如在 setter 中)的动机是该方法没有副作用。返回 true 或 false 可能是一个副作用。Void 意味着该方法在按设计执行时没有副作用。返回异常的 void 方法是好的,因为异常不是方法正常处理的副作用。
A method returning void implies that it is by definition an isolated working method. A chain of void methods is a chain of loosely coupled methods, because no other method can rely on the outcome of its predecessor as no method has any outcome. There is a design pattern for this, namely the Chain of Responsibility design pattern. Chaining different loggers is a traditional example and the call to the subsequent filters within the servlet api.
返回 void 的方法意味着根据定义它是一种隔离的工作方法。void 方法链是松耦合方法链,因为没有其他方法可以依赖其前身的结果,因为没有方法有任何结果。为此有一个设计模式,即责任链设计模式。链接不同的记录器是一个传统的例子,它调用 servlet api 中的后续过滤器。
To chain void methods in a meaningful way implies that the shared object on which those methods work is after each step in a state that is understandable by the void method working on the object. All subsequent calls may not rely on the outcome of its predecessor nor influence the working of the calls after its own call. The easiest way you can ensure that is by not letting them change the internal state of the object (like the logger example), or let each method change another part of the internal state of the object.
以一种有意义的方式链接 void 方法意味着这些方法在其上工作的共享对象在每个步骤之后都处于一种状态,该状态可由在该对象上工作的 void 方法理解。所有后续调用不得依赖于其前任的结果,也不可能影响其自身调用之后的调用的工作。您可以确保的最简单方法是不让它们更改对象的内部状态(如记录器示例),或者让每个方法更改对象内部状态的另一部分。
Void methods to be chained are in my opinion only those methods that have flavor 2 and share some type of processing. I would not chain the void methods concerning the life cycle of the class, because each method is a change of a different part of the whole state of the object. The presence of those methods can change over time with any change of design of the class, hence do I not advice to chain those methods. Furthermore are all exceptions thrown by the first type of void methods independent of each other, while you may expect that the exceptions thrown by the chained void methods of flavor 2 sharing some type of processing have something in common.
在我看来,要链接的 Void 方法只是那些具有风格 2 并共享某种处理类型的方法。我不会链接有关类生命周期的 void 方法,因为每个方法都是对象整个状态的不同部分的更改。这些方法的存在会随着类设计的任何变化而随着时间的推移而改变,因此我不建议链接这些方法。此外,第一种类型的 void 方法抛出的所有异常都是相互独立的,而您可能期望共享某种处理类型的风格 2 的链式 void 方法抛出的异常有一些共同点。