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.
Method chaining eliminates an extra variable for each intermediate step. The developer is saved from the cognitive burden of naming the variable and keeping the variable in mind.
Method Chaining is the practice of calling different methods in a single line instead of calling other methods with the same object reference separately. Under this procedure, we have to write the object reference once and then call the methods by separating them with a (dot.).
Method chaining is a technique in which methods are called on a sequence to form a chain and each of these methods return an instance of a class. These methods can then be chained together so that they form a single statement. A fluent interface is an object-oriented API that depends largely on method chaining.
Method chaining is a programmatic style of invoking multiple method calls sequentially with each call performing an action on the same object and returning it. It eliminates the cognitive burden of naming variables at each intermediate step.
Drawbacks
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.
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:
Command
. Command does not return a value.Query
. Query does not modify the data.Following the command-query API will give you the benefits like:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With