25

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.

Community
  • 1
  • 1
MartinTeeVarga
  • 10,478
  • 12
  • 61
  • 98

9 Answers9

27

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

Richard Tingle
  • 16,906
  • 5
  • 52
  • 77
  • +1 for concisely pointing out 2 problems but I don't agree with the readability issue because you can still add the linefeeds like in Amits example – user829755 Jun 13 '13 at 19:20
  • @user829755 You are probably right, I am equating "allows you to do bad things" with "causes you to do bad things". – Richard Tingle Jun 13 '13 at 19:46
  • +1 Thanks, now I see the point behind your comment on the other answer. – MartinTeeVarga Jun 14 '13 at 01:54
  • I have changed Vector to Vector2d in your answer to prevent the confusion and removed it from the footnote. Vector2d is commonly used in Java for such class and I hope most of the people will understand it. Please consider it before accepting the edit. – MartinTeeVarga Jun 14 '13 at 01:56
  • @sm4 Thanks sm4. Your edit appears to have gone before I could look at it. However, I deliberately chose a fictional class so I could give it both non local and local methods with both chainable and non chainable versions. The real Vector3d class doesn't have all 3 of these so I didnt want people to get bogged down in the fact that my snippets wouldnt work – Richard Tingle Jun 14 '13 at 07:15
  • The confusion comes from the fact that Vector is a real class in Java and it is not clear how can you "multiply" it until you read your footnotes. Right, my edit was discarded by someone. – MartinTeeVarga Jun 14 '13 at 07:21
  • Ah, I avoid one real class and find annother. I'll have a look at it – Richard Tingle Jun 14 '13 at 07:30
26

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.

Tengiz
  • 8,011
  • 30
  • 39
  • 4
    +1 This sounds like a great idea for framework design. I hope you get some upvotes so I am not the only one who likes it. – MartinTeeVarga Jun 14 '13 at 01:53
8

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.

MartinTeeVarga
  • 10,478
  • 12
  • 61
  • 98
Harish Kumar
  • 528
  • 2
  • 15
6

You may like to read about Fluent Interface by Martin Fowler

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
Nitin Tripathi
  • 491
  • 2
  • 10
4

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.

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.

Amit
  • 281
  • 1
  • 6
  • 16
2

If you prefer immutability and functional programming, you never return 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.

Beryllium
  • 12,808
  • 10
  • 56
  • 86
  • While this is an interesting thought, returning the object itself for method chaining, wont change anything about it. It would still be called only for its side-effect. – martinstoeckli Jun 13 '13 at 13:24
  • @martinstoeckli You're right - I've added this in the context of immutability/functional programming, just to open a different perspective on this question. – Beryllium Jun 13 '13 at 13:59
1

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.

public class MyObject {

    // ... my data

    public MyObject copyTo(MyObject other) {

        //... copy data


        // what should I return?
        return this;
    }
}
Craig
  • 1,390
  • 7
  • 12
1

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.

buritos
  • 598
  • 2
  • 9
  • Considerable amount of work is not a good argument when writing a framework. You want to put as much work as possible if it makes your framework better, easier to use, more robust and stable. – MartinTeeVarga Jun 07 '13 at 09:30
  • 2
    @sm4 what if someone extends one of your framework classes? Then you've passed that work on to them – Richard Tingle Jun 07 '13 at 09:35
0

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.

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.

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.

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.

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.

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 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.

Loek Bergman
  • 2,192
  • 20
  • 18