问题描述
我对Java很感兴趣,但我认为这是一个普遍的问题.最近,我一直在使用使用大量方法链接的Arquillian Framework(ShrinkWrap).方法链的其他示例是StringBuilder,StringBuffer中的方法.使用这种方法有明显的好处:减少的冗长是其中之一.
现在我想知道,为什么没有所有具有void返回参数为链的方法?链接中必须有一些明显和客观的缺点.因为如果所有方法都是可链的,我仍然可以选择不使用它.
我不是在要求更改Java中的现有代码,这可能会破坏某个地方的某些内容,但是解释为什么它也不会使用它也是不错的.我从未来的框架(用Java编写)设计的角度来询问.
我也发现了一个类似的问题,但是原始的问答实际上想知道为什么它被认为是一个好习惯:方法链接 - 为什么是一个好习惯?
虽然有一些可用的答案,但我仍然不确定链接的所有好处和缺点是什么,以及是否可以将所有无效方法链接出来.
推荐答案
缺点
- 主要是混淆签名,如果有东西返回新的 实例我不希望它也是一种突变方法.例如 向量有一个比例方法,然后如果有回报,我会假定 它返回一个由输入缩放的新向量,如果不是,则我 期望它在内部扩展.
- 再加上,如果延长了课程,当然,您会遇到问题,而链接的对象则将其置于超级模型.当在父类中声明链接方法但在子类的实例中使用时,就会发生这种情况.
好处
-
它允许数学方程式样式代码作为完整方程式编写,而无需多个中间对象(导致不必要的开销),例如,没有方法将矢量三重交叉产品固定(作为一个随机的示例)必须写为
MyVector3d tripleCrossProduct=(vector1.multiply(vector2)).multiply(vector3);
具有创建必须创建并收集垃圾的中间对象的缺点,或者
MyVector3d tripleCrossProduct=vector1; tripleCrossProduct.multiplyLocal(vec2); tripleCrossProduct.multiplyLocal(vec3);
避免创建中间对象,但尚不清楚,变量名称tripleCrossProduct实际上是一个谎言,直到第3行3.但是,如果您有方法链,则可以简洁地写入此方法.一种普通的数学方式而不创建不必要的中间对象.
MyVector3d tripleCrossProduct=vector1.multiplyLocal(vector2).multiplyLocal(vector3);
所有这些都假定vector1是牺牲的,而不必再使用
-
,当然还有明显的好处;简洁.即使您的操作没有在我的上面示例的庄园中链接,您仍然可以避免对对象的不必要引用
SomeObject someObject=new SomeObject(); someObject .someOperation() .someOtherOperation();
nb MyVector3d不被用作真实的Java类,而是假定在调用.multiply()方法时执行交叉产品. .cross()不使用,因此"意图"对于不熟悉矢量计算的人
更清楚
NB Amit的解决方案是使用多行方法链接的第一个答案,我将其包括为完整性的第四子弹点的一部分
其他推荐答案
方法链接是实现流利接口的一种方式,无论编程语言如何.它的主要好处(可读代码)告诉您何时使用它.如果没有特别需要可读的代码,请更好地避免使用它,除非API自然设计以作为方法调用返回上下文/对象.
步骤1:流利的接口与命令 - 问题API
必须考虑对命令Query API的fluent接口.为了更好地理解它,让我写下以下命令问题API的子弹列表的定义.简单地说,这只是标准面向对象的编码方法:
- 修改数据的方法称为Command.命令不返回值.
- 返回值的方法称为Query.查询不会修改数据.
遵循命令Query API将为您带来以下好处:
- 查看面向对象的代码您了解正在发生的事情.
- 调试代码更容易,因为每个调用单独发生.
步骤2:命令标志性API顶部的流利接口
但是,出于某种原因存在命令 - 问题API,确实读取更好.那么,我们如何获得流利界面和命令Query API的好处?
答案:流利的接口必须在命令广播的顶部实现(而不是 fluent接口).将流利的界面视为命令Query API的立面.毕竟,它被称为流利的" 接口" - a 可读或便利性接口 api. >
通常,在准备好命令Query API(书面,可能是单位测试,抛光到轻松调试)之后,您可以在其顶部编写流利的接口软件层.换句话说,Fluent接口通过使用命令 - 问题API来实现其功能.然后,在您想要的方便和可读性的任何地方使用流利的界面(带有方法链接).但是,一旦您想了解实际发生的事情(例如,在调试异常时),您始终可以挖掘命令Query API-良好的旧对象代码.
其他推荐答案
我发现使用方法链的缺点是当发生NullPointerException或任何其他Exception发生时,请调试代码.假设您有以下代码:
String test = "TestMethodChain"; test.substring(0,10).charAt(11); //This is just an example
然后,在执行上述代码时,您将获得字符串索引以外的范围:异常.当您进入实时情况并发生此类情况时,您就会知道发生了哪个行错误,但链接方法的哪一部分造成了.因此,需要明智地使用它,因为它知道数据将始终出现或正确处理错误.
它具有其优点,就像您不需要编写多个代码并使用多个变量.
许多框架/工具都像推土机一样使用它,当我用来调试代码时,我必须查看链的每个部分才能查找导致错误的原因.
希望这会有所帮助.
问题描述
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.
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.
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.
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.
推荐答案
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.
Benefits
It allows mathematical equation style 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 but is 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();
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
其他推荐答案
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.
Step 1: Fluent Interface vs. 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:
- 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.
Following the command-query API will give you the benefits like:
- 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
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?
Answer: fluent interface must be implemented on top of the command-query API (as opposed to replacing the command-query API by the fluent interface). Think of a fluent interface as a facade over the command-query API. And after all, it's called fluent "interface" - a readable or convenience interface over the standard (command-query) 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.
其他推荐答案
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:
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.
Hope this helps.