Method chaining - why is it a good practice, or not?

Viewed 86863

Method chaining is the practice of object methods returning the object itself in order for the result to be called for another method. Like this:

participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save()

This seems to be considered a good practice, since it produces readable code, or a "fluent interface". However, to me it instead seems to break the object calling notation implied by the object orientation itself - the resulting code does not represent performing actions to the result of a previous method, which is how object oriented code is generally expected to work:

participant.getSchedule('monday').saveTo('monnday.file')

This difference manages to create two different meanings for the dot-notation of "calling the resulting object": In the context of chaining, the above example would read as saving the participant object, even though the example is in fact intended to save the schedule object received by getSchedule.

I understand that the difference here is whether the called method should be expected to return something or not (in which case it would return the called object itself for chaining). But these two cases are not distinguishable from the notation itself, only from the semantics of the methods being called. When method chaining is not used, I can always know that a method call operates on something related to the result of the previous call - with chaining, this assumption breaks, and I have to semantically process the whole chain to understand what the actual object being called really is. For example:

participant.attend(event).setNotifications('silent').getSocialStream('twitter').postStatus('Joining '+event.name).follow(event.getSocialId('twitter'))

There the last two method calls refer to the result of getSocialStream, whereas the ones before refer to the participant. Maybe it's bad practice to actually write chains where the context changes (is it?), but even then you'll have to constantly check whether dot-chains that look similar are in fact keep within the same context, or only work on the result.

To me it appears that while method chaining superficially does produce readable code, overloading the meaning of the dot-notation only results in more confusion. As I don't consider myself a programming guru, I assume the fault is mine. So: What am I missing? Do I understand method chaining somehow wrong? Are there some cases where method chaining is especially good, or some where it's especially bad?

Sidenote: I understand this question could be read as a statement of opinion masked as a question. It, however, isn't - I genuinely want to understand why chaining is considered good practice, and where do I go wrong in thinking it breaks the inherent object-oriented notation.

18 Answers

I agree that this is subjective. For the most part I avoid method chaining, but recently I also found a case where it was just the right thing - I had a method which accepted something like 10 parameters, and needed more, but for the most time you only had to specify a few. With overrides this became very cumbersome very fast. Instead I opted for the chaining approach:

MyObject.Start()
    .SpecifySomeParameter(asdasd)
    .SpecifySomeOtherParameter(asdasd)
    .Execute();

The method chaining approach was optional, but it made writing code easier (especially with IntelliSense). Mind you that this is one isolated case though, and is not a general practice in my code.

The point is - in 99% cases you can probably do just as well or even better without method chaining. But there is the 1% where this is the best approach.

Personally, I prefer chaining methods that only act on the original object, e.g. setting multiple properties or calling utility-type methods.

foo.setHeight(100).setWidth(50).setColor('#ffffff');
foo.moveTo(100,100).highlight();

I do not use it when one or more of the chained methods would return any object other than foo in my example. While syntactically you can chain anything as long as you are using the correct API for that object in the chain, changing objects IMHO makes things less readable and can be really confusing if the APIs for the different objects have any similarities. If you do some really common method call at the end (.toString(), .print(), whatever) which object are you ultimately acting upon? Someone casually reading the code might not catch that it would be an implicitly returned object in the chain rather than the original reference.

Chaining different objects can also lead to unexpected null errors. In my examples, assuming that foo is valid, all the method calls are "safe" (e.g., valid for foo). In the OP's example:

participant.getSchedule('monday').saveTo('monnday.file')

...there's no guarantee (as an outside developer looking at the code) that getSchedule will actually return a valid, non-null schedule object. Also, debugging this style of code is often a lot harder since many IDEs will not evaluate the method call at debug time as an object that you can inspect. IMO, anytime you might need an object to inspect for debugging purposes, I prefer to have it in an explicit variable.

Martin Fowler has a good discussion here:

Method Chaining

When to use it

Method Chaining can add a great deal to the readability of an internal DSL and as a result has become almost a synonum for internal DSLs in some minds. Method Chaining is best, however, when it's used in conjunction with other function combinations.

Method Chaining is particularly effective with grammars like parent::= (this | that)*. The use of different methods provides readable way of seeing which argument is coming next. Similarly optional arguments can be easily skipped over with Method Chaining. A list of mandatory clauses, such as parent::= first second doesn't work so well with the basic form, although it can be supported well by using progressive interfaces. Most of the time I'd prefer Nested Function for that case.

The biggest problem for Method Chaining is the finishing problem. While there are workarounds, usually if you run into this you're better off usng a Nested Function. Nested Function is also a better choice if you are getting into a mess with Context Variables.

In my opinion, method chaining is a bit of a novelty. Sure, it looks cool but I don't see any real advantages in it.

How is:

someList.addObject("str1").addObject("str2").addObject("str3")

any better than:

someList.addObject("str1")
someList.addObject("str2")
someList.addObject("str3")

The exception might be when addObject() returns a new object, in which case the unchained code may be a little more cumbersome like:

someList = someList.addObject("str1")
someList = someList.addObject("str2")
someList = someList.addObject("str3")

Edit: My opinions on this have changed over the last 10 years. For mutable objects, I still don't see a lot of benefit, although it is useful for avoiding a little bit of duplication. But now that I favour immutability a lot more, method chaining is my preferred way of doing non-destructive updates, which I use all the time.

Many use method chaining as a form of convenience rather than having any readability concerns in mind. Method chaining is acceptable if it involves performing the same action on the same object - but only if it actually enhances readability, and not just for writing less code.

Unfortunately many use method chaining as per the examples given in the question. While they can still be made readable, they are unfortunately causing high coupling between multiple classes, so it's not desirable.

It is dangerous because you may be depending on more objects than expected, like then your call returns an instance of another class:

I will give an example:

foodStore is an object that is composed of many food stores you own. foodstore.getLocalStore() returns an object that holds information on the closest store to the parameter. getPriceforProduct(anything) is a method of that object.

So when you call foodStore.getLocalStore(parameters).getPriceforProduct(anything)

you are depending not only on FoodStore as you though, but also on LocalStore.

Should getPriceforProduct(anything) ever changes, you need to change not only FoodStore but also the class that called the chained method.

You should always aim for loose coupling among classes.

That being said, i personally like to chain them when programming Ruby.

This seems kinda subjective.

Method chaining is not soemthing that is inherently bad or good imo.

Readability is the most important thing.

(Also consider that having large numbers of methods chained will make things very fragile if something changes)

Method chaining may simply be a novelty for most cases but I think it has it's place. One example might be found in CodeIgniter's Active Record use:

$this->db->select('something')->from('table')->where('id', $id);

That looks a lot cleaner (and makes more sense, in my opinion) than:

$this->db->select('something');
$this->db->from('table');
$this->db->where('id', $id);

It really is subjective; Everyone has their own opinion.

I generally hate method chaining because I think it worsens readability. Compactness is often confused with readability, but they are not the same terms. If you do everything in a single statement then that is compact, but it is less readable (harder to follow) most of the times than doing it in multiple statements. As you noticed unless you cannot guarantee that the return value of the used methods are the same, then method chaining will be a source of confusion.

1.)

participant
    .addSchedule(events[1])
    .addSchedule(events[2])
    .setStatus('attending')
    .save();

vs

participant.addSchedule(events[1]);
participant.addSchedule(events[2]);
participant.setStatus('attending');
participant.save()

2.)

participant
    .getSchedule('monday')
        .saveTo('monnday.file');

vs

mondaySchedule = participant.getSchedule('monday');
mondaySchedule.saveTo('monday.file');

3.)

participant
    .attend(event)
    .setNotifications('silent')
    .getSocialStream('twitter')
        .postStatus('Joining '+event.name)
        .follow(event.getSocialId('twitter'));

vs

participant.attend(event);
participant.setNotifications('silent')
twitter = participant.getSocialStream('twitter')
twitter.postStatus('Joining '+event.name)
twitter.follow(event.getSocialId('twitter'));

As you can see you win close to nothing, because you have to add line breaks to your single statement to make it more readable and you have to add indentation to make it clear that you are talking about different objects. Well if I'd want to use an identation based language, then I would learn Python instead of doing this, not to mention that most of the IDEs will remove the indentation by auto formatting the code.

I think the only place where this kind of chaining can be useful is piping streams in CLI or JOINing multiple queries together in SQL. Both have a price for multiple statements. But if you want to solve complex problems you will end up even with those paying the price and writing the code in multiple statements using variables or writing bash scripts and stored procedures or views.

As of the DRY interpretations: "Avoid the repetition of knowledge (not the repetition of text)." and "Type less, don't even repeat texts.", the first one what the principle really means, but the second one is common misunderstanding because many people cannot understand overcomplicated bullshit like "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system". The second one is compactness at all cost, which breaks in this scenario, because it worsens readability. The first interpretation breaks by DDD when you copy code between bounded contexts, because loose coupling is more important in that scenario.

Opinionated Answer

The biggest drawback of chaining is that it can be hard for the reader to understand how each method affects the original object, if it does, and what type does every method return.

Some questions:

  • Do methods in the chain return a new object, or the same object mutated?
  • Do all methods in the chain return the same type?
  • If not, how is indicated when a type in the chain changes?
  • Can the value returned by the last method be safely discarded?

Debugging, in most languages, can indeed be harder with chaining. Even if each step in the chain is on its own line (which kind of defeats the purpose of chaining), it can be hard to inspect the value returned after each step, specially for non-mutating methods.

Compile times can be slower depending on the language and compiler, as expressions can be much more complex to resolve.

I believe that like with everything, chaining is a good solution that can be handy in some scenario. It should be used with caution, understanding the implications, and limiting the number of chain elements to a few.

The totally missed point here, is that method chaining allows for DRY. It's an effective stand-in for the "with" (which is poorly implemented in some languages).

A.method1().method2().method3(); // one A

A.method1();
A.method2();
A.method3(); // repeating A 3 times

This matters for the same reason DRY always matters; if A turns out to be an error, and these operations need to be performed on B, you only need to update in 1 place, not 3.

Pragmatically, the advantage is small in this instance. Still, a little less typing, a litle more robust (DRY), I'll take it.

The good:

  1. It's terse, yet allows you to put more into a single line elegantly.
  2. You can sometimes avoid the use of a variable, which may occasionally be useful.
  3. It may perform better.

The bad:

  1. You're implementing returns, essentially adding functionality to methods on objects that isn't really a part of what those methods are meant to do. It's returning something you already have purely to save a few bytes.
  2. It hides context switches when one chain leads to another. You can get this with getters, except it's pretty clear when the context switches.
  3. Chaining over multiple lines looks ugly, doesn't play well with indentation and can cause some operator handling confusion (especially in languages with ASI).
  4. If you want to start returning something else that's useful for a chained method, you're potentially going to have a harder time fixing it or hit more problems with it.
  5. You're offloading control to an entity that you wouldn't normally offload to purely for convenient, even in strictly typed languages mistakes caused by this cannot always be detected.
  6. It may perform worse.

General:

A good approach is to not use chaining in general until situations arise or specific modules would be particularly suited to it.

Chaining can hurt readability quite severely in some cases especially when weighing in point 1 and 2.

On accasation it can be misused, such as instead of another approach (passing an array for example) or mixing methods in bizarre ways (parent.setSomething().getChild().setSomething().getParent().setSomething()).

In typed languages (that lack auto or equivalent) this saves the implementer from having to declare the types of the intermediate results.

import Participant
import Schedule

Participant participant = new Participant()
... snip...
Schedule s = participant.getSchedule(blah)
s.saveTo(filename)

For longer chains you might be dealing with several different intermediate types, you'd need to declare each of them.

I believe that this approach really developed in Java where a) all function calls are member function invocations, and b) explicit types are required. Of course there is a trade-off here in terms, loosing some explicitness, but in some situations some people find it worth while.

Related