2

That question bothers me for years, so, imagine some code that in 99% looks like a non-sequential (order does not matter) list of some specific objects method calls - and those calls are independent one from each other, like:

conf.addCollection(...);
conf.addRule(...);
conf.registerEvent(...);

I can implement method chaining to have something like:

 conf.addCollection(...)
     .addRule(...)
     .registerEvent(...);

but should I? In other words - should I implement method chaining only for the sake of not referring same variable multiple times? If I should not in this particular case, still, what are empirical rules when it's better to have chaining?

1

1 Answer 1

1

The problem with this is that you change the return type of the method in a non-obvious way. The method's return type is Conf, but in reality, it doesn't actually return a useful value at all. Someone who looks at the API documentation might then wonder why the method returns Conf, when in reality, the method has only a side-effect and no useful return.

Which brings me to the second problem: this only works if the methods have side-effects and no useful return value. But, you should strive to write purely functional, referentially-transparent methods without side-effects, so this promotes bad design.

The first problem can be alleviated in a language with a precise enough type system. For example, in Scala, you could make the return type the singleton type this.type, which means that the method returns this and only this (it cannot return a different object of the same type as this, it can only return this itself). So, you would define your methods like this:

class Conf {
  def +=(coll: Collection): this.type = {
    // do stuff with `coll`
    this
  }

  def +=(rule: Rule): this.type = {
    // do stuff with `rule`
    this
  }

  def +=(event: Event): this.type = {
    // do stuff with `event`
    this
  }
}

and use it like this:

conf += someCollection += someRule += someEvent

[Note: I chose an overloaded method for the different types, you could obviously use different names instead, if you want, then it would be something like conf addCollection someCollection addRule someRule registerEvent someEvent.]

This still leaves the second problem, however: it only makes sense with side-effects.

Many languages have message cascades that allow you to send multiple messages to the same receiver, e.g. in Smalltalk, your example could be written as:

conf 
  addCollection: aCollection;
  addRule:       aRule;
  registerEvent: anEvent.

So, there is simply no need for message chaining in this way.

In Ruby, you could (ab)use BasicObject#instance_eval, although I would recommend against it:

conf.instance_eval do
  add_collection a_collection
  add_rule       a_rule
  register_event an_event
end

I would mostly avoid doing this, since it implies side-effects and side-effects should be avoided in general, unless absolutely necessary. If you do this, it should be clearly documented, ideally via the type system.

Note: message chaining where every message in the chain returns a new object and is purely functional and referentially-transparent without side-effects, that is a whole different matter. As long as it doesn't violate the Law of Demeter for Functions, that is perfectly okay.

In a Fluent DSL, where it is clear that the return types of methods don't make sense in isolation and should only be understood as part of the DSL structure as a whole, the situation is different. But, in a Fluent DSL, you would typically return a new builder object every time anyway, to help with type inference, intelligent code completion, etc. I gave an example of this in this answer to a question about Fluent DSLs (more precisely, Fluent Builders).

4
  • Someone who looks at the API documentation might then wonder why the method returns Conf It's not our job to cater to the lowest common denominator of programming. If a professional developer doesn't know the fluent interface pattern, that's on them.
    – Alexander
    Commented Jun 30, 2017 at 17:24
  • I adressed this in the third paragraph (use a language that allows you to specify a more precise type that explicitly spells out that fact that it is a Fluent DSL) and in the last paragraph (document the fact that it is a Fluent DSL and that the return types shouldn't be viewed in isolation but rather as ways of connecting the different parts of a "Fluent Sentence" together). But, honestly, tell me why a professional developer who happens to know mostly languages with message cascades should know the Fluent Interface pattern? He would just use a message cascade instead. Commented Jul 1, 2017 at 7:40
  • Martin Fowler has this to say: "One of the problems of methods in a fluent interface is that they don't make much sense on their own. Looking at a method browser of method by method documentation doesn't show much sense to with. Indeed sitting there on its own I'd argue that it's a badly named method that doesn't communicate its intent at all well. It's only in the context of the fluent action that it shows its strengths. One way around this may be to use builder objects that are only used in this context." Commented Jul 1, 2017 at 7:41
  • And this: "I've also noticed a common misconception - many people seem to equate fluent interfaces with Method Chaining. Certainly chaining is a common technique to use with fluent interfaces, but true fluency is much more than that. The JMock example I show above uses method chaining, but also nested functions and object scoping." – Note that the question explicitly talks about Method Chaining and not a Fluent Interface. So, how would a professional developer recognize the Fluent Interface pattern in something that isn't a Fluent Interface but rather simple Method Chaining? Commented Jul 1, 2017 at 7:42

Not the answer you're looking for? Browse other questions tagged or ask your own question.