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

20

192

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.

Oreopithecus answered 9/7, 2009 at 13:44 Comment(8)
They also call these "fluent" methods or a "fluent" interface. You might want to update your title to use this term.Hellhound
In another SO discussion it was said that fluent interface is a larger concept that has to do with code readibility, and method chaining is only one way to aim towards this. They are closely related, though, so I did add the tag and referenced fluent interfaces in text - I think these are sufficient.Oreopithecus
The way I've come to think of this, is that method chaining is, in fact, a hackaround to get a missing feature into the language syntax. It really woudn't be needed if there was a built-in alternative notation to . that would ignore any mehtod return values and always invoke any chained methods using the same object.Oreopithecus
The fluent syntax of Microsoft's LINQ is a demonstration of the power of method chaining that is available as a fundamental component of the .NET Framework. Also see Is it worth to use method chaining in C#?.Lathe
Another very well-known example of method chaining is the jQuery JavaScript library.Lathe
I disagree with this premise: "the resulting code does not represent performing actions to the result of a previous method". Sure it does. The result of participant.AddSchedule(events[1]) is a participant who's just had events[1] added to their schedule.Bucharest
Method chaining is a way to avoid repeating yourself, and "Don't Repeat Yourself" is as close to a universally accepted maxim of good software development practice as one can find.Isaacisaacs
You may find this blog post of mine relevant: The Law of Demeter Doesn't Mean One DotKist
C
97

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.

Contortionist answered 9/7, 2009 at 14:0 Comment(13)
IMO, the best way to use method chaining in this scenario is to create a parameter object to be passed to the function, like P = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);. You have the advantage of being able to reutilize this parameter object in other calls, which is a plus!Encarnalize
Just my cent of contribution about the patterns, factory method usually has only one creation point and the products are an static choose based on the parameters of the factory method. The chain creation looks more to a Builder pattern where you call different methods to obtain a result, the methods can be optional as in the Method chaining, we can have something like PizzaBuilder.AddSauce().AddDough().AddTopping() more references hereLongueur
Method chaining (as chown in the original question) is considered bad when it violates the law of demeter. See: ifacethoughts.net/2006/03/07/… The answer given here in fact follows that law as it is a "builder pattern".Granulation
@Marco Medrano The PizzaBuilder example always bugged me since I read it in JavaWorld ages ago. I feel I should be adding sauce to my Pizza, not to my chef.Orsola
@B.Dalton - Think of it this way - it's the PizzaBuilder that does the adding, not that something is added to the PizzaBuilder. In the end, the PizzaBuilder will produce a Pizza. But a Pizza can't add a topping to itself. :)Contortionist
I know what you mean, Vilx. But yet when I read list.add(someItem), I read that as "this code is adding someItem to the list object". so when I read PizzaBuilder.AddSauce(), I read this naturally as "this code is adding sauce to the PizzaBuilder object". In other words, I see the orchestrator (the one doing the adding) as the method in which the code list.add(someItem) or PizzaBuilder.addSauce() is embedded. But I just think the PizzaBuilder example is a little artificial. Your example of MyObject.SpecifySomeParameter(asdasd)works fine for me.Orsola
@B.Dalton - OK, fair point. Well, as I said, it's subjective. For some people it works, for others it's more difficult.Contortionist
@B.Dalton I got what you mean. Think that there is a Director (boss, customer) commanding the PizzaBuilder. Hey builder add this to my pizza now this and this. Finally the Director will execute the last command which is GetResult() or "give me the pizza you built for me". So the PizzaBuilder is not adding to itself the sauce it is adding to the basic product which at the end will be completely built and delivered to the Director.Longueur
@Marco Medrano Yes, Marco, that's exactly it. And the AddXXX is the bit that bugs me. It already has a different semantic meaning to me (i.e. add XXX to the object on the left of the dot). I was thinking about how to improve that, and I'd change AddSauce etc to UsingSauce etc (or WithXXX), so it would read more naturally (to me) as PizzaBuilder.UsingDough().UsingSauce().UsingTopping().MakePizza();Orsola
@B.Dalton yeah your solution is readable, if the team is comfortable with that, it makes sense to me !Longueur
There is a non-subjective side of method chaining having nothing to do with style preferences: Fluent only can work if all methods in the chain guarantee to never return null. In many projects I have to review/fix, method-chaining using methods that may return null is a leading cause of hard-to-pinpoint NullPointerException defects.Pigeonhearted
but why can't they just be individual calls? MyObject.Start() MyObject.SpecifySomeParameter(asdasd) MyObject.SpecifySomeOtherParameter(asdasd) MyObject.Execute(); ? what makes it so beneficial or necessary to chain?Thermopylae
@Thermopylae - Sure, you can do that too. I just like how this code looks. It's kind of... a personal style preference, I guess? But there's really no difference in the end result.Contortionist
U
101

Just my 2 cents;

Method chaining makes debugging tricky: - You can't put the breakpoint in a concise point so you can pause the program exactly where you want it - If one of these methods throws an exception, and you get a line number, you have no idea which method in the "chain" caused the problem.

I think it's generally good practice to always write very short and concise lines. Every line should just make one method call. Prefer more lines to longer lines.

EDIT: comment mentions that method chaining and line-breaking are separate. That is true. Depending on the debugger though, it may or may not be possible to place a break point in the middle of a statement. Even if you can, using separate lines with intermediate variables gives you a lot more flexibility and a whole bunch of values you can examine in the Watch window that helps the debugging process.

Ulric answered 28/10, 2010 at 3:56 Comment(10)
Aren't use of newlines and method chaining independent? You can chain with each call on a newline as per @Vilx- answer, and you can typically put multiple separate statements on the same line (eg. using semicolons in Java).Four
If all classes using chaining implement an interface such as interface Chainable { Chainable chainID(Object o_cid); Object getChainID(); }, then it is very easy to determine where the error occurred. It must be designed into the foundation of your code, though.Turning
This is reply is fully justified, but it just shows a weakness present in all debuggers I know and is not related specifically to the question.Projector
@Brabster. They may be separate for certain debuggers, however, having separate calls with intermediate variables still gives you a much larger wealth of information while you investigate bugs.Ulric
+1, at no point in time do you know what each method returned when step-debugging a method chain. The method chain pattern is a hack. It's a maintenance programmer's worst nightmare.Rainout
I am not a big fan of chaining either but why not simply put breakpoint inside the method definitions?Cellini
This is the only correct answer and should be the accepted answer. Most of the other answers here on this thread are only concerned with the process of writing code. You have to account for the entire product life cycle when working with software. A company will spend more time maintaining code over a products lifetime than actually writing code.Aimeeaimil
-1 Even on a single line some/many debuggers (in 2022) allow ctrl clicking (or similar) on a specific call in the chain to continue execution up to that call. Next stepping into the call allow debugging the this scope. I appreciate that this wasn't/might not have been the case in 2010 though, so just downvoting as this doesn't feel relevant anymore.Ganglion
This is the main reason I give for avoiding chaining - Method chaining makes debugging tricky: - You can't put the breakpoint in a concise point so you can pause the program exactly where you want it - If one of these methods throws an exception, and you get a line number, you have no idea which method in the "chain" caused the problem. Absolutely spot-on RAYBlurt
For the first comment is not correct, you can add a breakpoint (tracepoint) for Linq statements. Just with your cursor select the statement inside the Linq Action or Func method, and then righ click -> Breakpoints -> Insert TracepointLynettelynn
C
97

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.

Contortionist answered 9/7, 2009 at 14:0 Comment(13)
IMO, the best way to use method chaining in this scenario is to create a parameter object to be passed to the function, like P = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);. You have the advantage of being able to reutilize this parameter object in other calls, which is a plus!Encarnalize
Just my cent of contribution about the patterns, factory method usually has only one creation point and the products are an static choose based on the parameters of the factory method. The chain creation looks more to a Builder pattern where you call different methods to obtain a result, the methods can be optional as in the Method chaining, we can have something like PizzaBuilder.AddSauce().AddDough().AddTopping() more references hereLongueur
Method chaining (as chown in the original question) is considered bad when it violates the law of demeter. See: ifacethoughts.net/2006/03/07/… The answer given here in fact follows that law as it is a "builder pattern".Granulation
@Marco Medrano The PizzaBuilder example always bugged me since I read it in JavaWorld ages ago. I feel I should be adding sauce to my Pizza, not to my chef.Orsola
@B.Dalton - Think of it this way - it's the PizzaBuilder that does the adding, not that something is added to the PizzaBuilder. In the end, the PizzaBuilder will produce a Pizza. But a Pizza can't add a topping to itself. :)Contortionist
I know what you mean, Vilx. But yet when I read list.add(someItem), I read that as "this code is adding someItem to the list object". so when I read PizzaBuilder.AddSauce(), I read this naturally as "this code is adding sauce to the PizzaBuilder object". In other words, I see the orchestrator (the one doing the adding) as the method in which the code list.add(someItem) or PizzaBuilder.addSauce() is embedded. But I just think the PizzaBuilder example is a little artificial. Your example of MyObject.SpecifySomeParameter(asdasd)works fine for me.Orsola
@B.Dalton - OK, fair point. Well, as I said, it's subjective. For some people it works, for others it's more difficult.Contortionist
@B.Dalton I got what you mean. Think that there is a Director (boss, customer) commanding the PizzaBuilder. Hey builder add this to my pizza now this and this. Finally the Director will execute the last command which is GetResult() or "give me the pizza you built for me". So the PizzaBuilder is not adding to itself the sauce it is adding to the basic product which at the end will be completely built and delivered to the Director.Longueur
@Marco Medrano Yes, Marco, that's exactly it. And the AddXXX is the bit that bugs me. It already has a different semantic meaning to me (i.e. add XXX to the object on the left of the dot). I was thinking about how to improve that, and I'd change AddSauce etc to UsingSauce etc (or WithXXX), so it would read more naturally (to me) as PizzaBuilder.UsingDough().UsingSauce().UsingTopping().MakePizza();Orsola
@B.Dalton yeah your solution is readable, if the team is comfortable with that, it makes sense to me !Longueur
There is a non-subjective side of method chaining having nothing to do with style preferences: Fluent only can work if all methods in the chain guarantee to never return null. In many projects I have to review/fix, method-chaining using methods that may return null is a leading cause of hard-to-pinpoint NullPointerException defects.Pigeonhearted
but why can't they just be individual calls? MyObject.Start() MyObject.SpecifySomeParameter(asdasd) MyObject.SpecifySomeOtherParameter(asdasd) MyObject.Execute(); ? what makes it so beneficial or necessary to chain?Thermopylae
@Thermopylae - Sure, you can do that too. I just like how this code looks. It's kind of... a personal style preference, I guess? But there's really no difference in the end result.Contortionist
B
51

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.

Balch answered 10/7, 2009 at 14:42 Comment(1)
If there is chance that a Participant doesn't have a valid Schedule then getSchedule method is designed to return a Maybe(of Schedule) type and saveTo method is designed to accept a Maybe type.Slapjack
K
29

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.

Kaon answered 9/7, 2009 at 14:9 Comment(2)
What do you mean with DSL? Domain Specific LanguageNihi
@Sören: Fowler refers do domain-specific languages.Kaon
C
27

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.

Cuman answered 9/7, 2009 at 13:52 Comment(10)
It's more concise, as you avoid two of the 'someList' parts even in your first example and end up with one line instead of three. Now if that is actually good or bad depends on different things and perhaps is a matter of taste.Sylvestersylvia
The real advantage of having 'someList' only once, is that it's then much easier to give it a longer, more descriptive name. Any time a name needs to appear multiple times in quick succession, there's a tendency to make it short (to reduce repetition and improve readability), which makes it less descriptive, hurting readability.Unstrung
Good point about readability and DRY principles with the caveat that method-chaining prevents introspection of the return value of a given method and/or implies a presumption of empty lists/sets and non-null values from every method in the chain (a fallacy causing many NPEs in systems I have to debug/fix often).Pigeonhearted
@ChrisDodd Never heard of auto completion?Keffer
@inf3rno: How does autocompletion help readability? If anything its the opposite -- makes it easy to write long, unreadable messes that only the computer can understand.Unstrung
@ChrisDodd I meant that it reduces repetition by typing. I don't prefer very long variable names because either they are a sign of overengineering or that the developer does not understand what "context" and "local" mean. I think that hurts readability, not making them shorter. I don't think the name repetition has a significant effect on readability, the human brain is very good at recognizing text repetition if it has the same indentation. On the other hand method chaining makes the return values implicit and tries to solve complex problems in a single statement, which worsens readability.Keffer
"the human brain is very good at recognizing text repetition if it has the same indentation" -- that's precisely the problem with repetition: it causes the brain to focus on the repeating pattern. So to read AND UNDERSTAND the code you have to force your brain to look beyond/behind the repeating pattern to see what is really going on, which is in the differences between the repeating pattern that your brain tends to gloss over. This is WHY repetition is so bad for code readability.Unstrung
despite inf3rnos somewhat smirk comment LoL. I'm leaning towards several rows rather then one row. Because that one row can become awfully long (thinking 80 char mark rule). Also, don´t know about you guys, but one per line is divided nicely for my brain to take in and I see instantly its three times, while the long row actually makes my brain skip and then backtrack and count each occurrence carefully just to reashure.Tailstock
if it's length we are concerned about, a list is once, but method chaining means you'll have to create n many method definitions, all just a stupidly simple setter - that's NOT more concise than a simple primitive listThermopylae
message = get_default_dict() message.update({'alpha': 0.4}) send(message) vs send(get_default_dict().update({'alpha': 0.4})) (where we pretend update returns the updated dictionary). The second may be a bit harder to debug with print statements, but it has the advantage of not cluttering of the variable space and editor space with intermediate throw-away variables, without the silliness of throwing it in a separate function.Bowls
L
8

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.

Lynea answered 9/7, 2009 at 16:7 Comment(0)
W
8

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.

Wallachia answered 10/7, 2009 at 3:31 Comment(0)
P
8

Method chaining can allow for designing advanced DSLs in Java directly. In essence, you can model at least these types of DSL rules:

1. SINGLE-WORD
2. PARAMETERISED-WORD parameter
3. WORD1 [ OPTIONAL-WORD]
4. WORD2 { WORD-CHOICE-A | WORD-CHOICE-B }
5. WORD3 [ , WORD3 ... ]

These rules can be implemented using these interfaces

// Initial interface, entry point of the DSL
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 word1();
  Intermediate2 word2();
  Intermediate3 word3();
}

// Terminating interface, might also contain methods like execute();
interface End {}

// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}

// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}

// Intermediate interface returning itself on word3(), in order to allow for
// repetitions. Repetitions can be ended any time because this interface
// extends End
interface Intermediate3 extends End {
  Intermediate3 word3();
}

With these simple rules, you can implement complex DSL's such as SQL directly in Java, as is done by jOOQ, a library that I have created. See a rather complex SQL example taken from my blog here:

create().select(
    r1.ROUTINE_NAME,
    r1.SPECIFIC_NAME,
    decode()
        .when(exists(create()
            .selectOne()
            .from(PARAMETERS)
            .where(PARAMETERS.SPECIFIC_SCHEMA.equal(r1.SPECIFIC_SCHEMA))
            .and(PARAMETERS.SPECIFIC_NAME.equal(r1.SPECIFIC_NAME))
            .and(upper(PARAMETERS.PARAMETER_MODE).notEqual("IN"))),
                val("void"))
        .otherwise(r1.DATA_TYPE).as("data_type"),
    r1.NUMERIC_PRECISION,
    r1.NUMERIC_SCALE,
    r1.TYPE_UDT_NAME,
    decode().when(
    exists(
        create().selectOne()
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.notEqual(r1.SPECIFIC_NAME))),
        create().select(count())
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.lessOrEqual(r1.SPECIFIC_NAME)).asField())
    .as("overload"))
.from(r1)
.where(r1.ROUTINE_SCHEMA.equal(getSchemaName()))
.orderBy(r1.ROUTINE_NAME.asc())
.fetch()

Another nice example is jRTF, a little DSL designed for cerating RTF documents directly in Java. An example:

rtf()
  .header(
    color( 0xff, 0, 0 ).at( 0 ),
    color( 0, 0xff, 0 ).at( 1 ),
    color( 0, 0, 0xff ).at( 2 ),
    font( "Calibri" ).at( 0 ) )
  .section(
        p( font( 1, "Second paragraph" ) ),
        p( color( 1, "green" ) )
  )
).out( out );
Phoenicia answered 5/1, 2012 at 15:40 Comment(1)
@user877329: Yes, it can be used in pretty much any object oriented programming language that knows something like interfaces and subtype polymorphismPhoenicia
L
7

Benefits of Chaining
ie, where I like to use it

One benefit of chaining that I did not see mentioned was the ability to use it during variable initiation, or when passing a new object to a method, not sure if this is bad practice or not.

I know this is contrived example but say you have the following classes

Public Class Location
   Private _x As Integer = 15
   Private _y As Integer = 421513

   Public Function X() As Integer
      Return _x
   End Function
   Public Function X(ByVal value As Integer) As Location
      _x = value
      Return Me
   End Function

   Public Function Y() As Integer
      Return _y
   End Function
   Public Function Y(ByVal value As Integer) As Location
      _y = value
      Return Me
   End Function

   Public Overrides Function toString() As String
      Return String.Format("{0},{1}", _x, _y)
   End Function
End Class

Public Class HomeLocation
   Inherits Location

   Public Overrides Function toString() As String
      Return String.Format("Home Is at: {0},{1}", X(), Y())
   End Function
End Class

And say you don't have access to the base class, or Say the default values are dynamic, based on time, etc. Yes you could instantiate then, then change the values but that can become cumbersome, especially if you're just passing the values to a method:

  Dim loc As New HomeLocation()
  loc.X(1337)
  PrintLocation(loc)

But isn't this just much easier to read:

  PrintLocation(New HomeLocation().X(1337))

Or, what about a class member?

Public Class Dummy
   Private _locA As New Location()
   Public Sub New()
      _locA.X(1337)
   End Sub
End Class

vs

Public Class Dummy
   Private _locC As Location = New Location().X(1337)
End Class

This is how I've been using chaining, and typically my methods are just for configuration, so they are only 2 lines long, set a value, then Return Me. For us it has cleaned up huge lines very hard to read and understand code into one line that read like a sentence. something like

New Dealer.CarPicker().Subaru.WRX.SixSpeed.TurboCharged.BlueExterior.GrayInterior.Leather.HeatedSeats

Vs Something like

New Dealer.CarPicker(Dealer.CarPicker.Makes.Subaru
                   , Dealer.CarPicker.Models.WRX
                   , Dealer.CarPicker.Transmissions.SixSpeed
                   , Dealer.CarPicker.Engine.Options.TurboCharged
                   , Dealer.CarPicker.Exterior.Color.Blue
                   , Dealer.CarPicker.Interior.Color.Gray
                   , Dealer.CarPicker.Interior.Options.Leather
                   , Dealer.CarPicker.Interior.Seats.Heated)

Detriment Of Chaining
ie, where I don't like to use it

I don't use chaining when there are a lot of parameters to pass to the routines, mainly because the lines get very long, and as the OP mentioned it can get confusing when you're calling routines to other classes to pass to one of the chaining methods.

There is also the concern that a routine would return invalid data, thus so far I've only used chaining when I'm returning the same instance being called. As was pointed out if you chain between classes you make debuging harder (which one returned null?) and can increase dependencies coupling among classes.

Conclusion

Like everything in life, and programming, Chaining is neither good, nor bad if you can avoid the bad then chaining can be a great benefit.

I try to follow these rules.

  1. Try not to chain between classes
  2. Make routines specifically for chaining
  3. Do only ONE thing in a chaining routine
  4. Use it when it improves readability
  5. Use it when it makes code simpler
Levitical answered 20/5, 2011 at 18:59 Comment(0)
S
6

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)

Stigmasterol answered 9/7, 2009 at 13:51 Comment(3)
It might indeed be subjective, hence the subjective tag. What I hope the answers will highlight to me is in which cases method chaining would be a good idea - right now I don't see much any, but I think this is just my failure to understand the good points of the concept, rather than something inherently bad in the chaining itself.Oreopithecus
Wouldn't it be inherently bad if it results in high coupling? Breaking the chain into individual statements does not lower readability.Wallachia
depends on how dogmatic you want to be. If it results in something more readable then this could be preferable in a lot of situations. The big problem I have with this approach is that most methods on an object will return references to the object itself but often the method will return a reference to a child object upon which you can chain more methods. Once you start to do this then it becomes very difficult for another coder to disentangle what is going on. Also any change in functionality of a method is going to be a pain to debug in a large compound statement.Stigmasterol
V
4

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.

Vex answered 10/7, 2009 at 12:58 Comment(1)
This is an example of a Fluent Interface with Method Chaining, so the UseCase is slightly different here. You are not just chaining but you are creating an internal domain specific language that reads easily. On a sidenote, CI's ActiveRecord is not an ActiveRecord.Snick
G
4

I think the primary fallacy is thinking this is an object oriented approach in general when in fact it is more of a functional programming approach than anything else.

The main reasons I use it is for both readability and preventing my code from being inundated by variables.

I don't really understand what others are talking about when they say it damages readability. It is one of the most concise and cohesive form of programming I have used.

Also this:

convertTextToVoice.LoadText("source.txt").ConvertToVoice("destination.wav");

is how I would typically use it. Using it to chain x number of parameters is not how I typically use it. If I wanted to put in x number of parameters in a method call I would use the params syntax:

public void foo(params object[] items)

And cast the objects based on type or just use a datatype array or collection depending on your use case.

Gimcrack answered 5/1, 2018 at 18:31 Comment(2)
+1 on " the primary fallacy is thinking this is an object oriented approach in general when in fact it is more of a functional programming approach than anything else". The prominent use-case is for state-less manipulations on objects (where instead of changing its state, you return a new object on which you continue to act). The OP's questions and other answers show stateful actions which indeed seem awkward with chaining.Wheelhouse
Yes you are right it is a state-less manipulation except I typically do no create a new object but use dependency injection instead to make it an available service. And yes stateful use cases are not what I think method chaining was intended for. The only exception I see is if you initialize the DI service with some settings and have some kind of watchdog to monitor state like a COM service of some kind. Just IMHO.Gimcrack
K
4

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.

Keffer answered 16/2, 2019 at 22:0 Comment(0)
S
2

I agree, I therefor changed the way an fluent interface was implemented in my library.

Before:

collection.orderBy("column").limit(10);

After:

collection = collection.orderBy("column").limit(10);

In the "before" implementation the functions modified the object and ended in return this. I changed the implementation to return a new object of the same type.

My reasoning for this change:

  1. The return value had nothing to do with the function, it was purely there to support the chaining part, It should have been a void function according to OOP.

  2. Method chaining in system libraries also implement it that way (like linq or string):

    myText = myText.trim().toUpperCase();
    
  3. The original object remains intact, allowing the API user to decide what to do with it. It allows for:

    page1 = collection.limit(10);
    page2 = collection.offset(10).limit(10);
    
  4. A copy implementation can also be used for building objects:

    painting = canvas.withBackground('white').withPenSize(10);
    

    Where the setBackground(color) function changes the instance and returns nothing (like its supposed to).

  5. The behavior of the functions are more predicable (See point 1 & 2).

  6. Using a short variable name can also reduce code-clutter, without forcing a api on the model.

    var p = participant; // create a reference
    p.addSchedule(events[1]);p.addSchedule(events[2]);p.setStatus('attending');p.save()
    

Conclusion:
In my opinion a fluent interface that uses an return this implementation is just wrong.

Skeen answered 27/8, 2011 at 14:26 Comment(3)
But wouldn't returning a new instance for each call create quite a bit of overhead, especially if you are using larger items? At least for non-managed languages.Levitical
@Levitical Performance-wise it's definitely faster to return this managed or otherwise. It's my opinion that it obtains a fluent api in a "not-natural" way. (Added reason 6: to show a non-fluent alternative, which doesn't have the overhead/added-functionality)Skeen
I agree with you. It is mostly better to leave a DSL's underlying state untouched and return new objects at every method call, instead... I'm curious: What's that library you've mentioned?Phoenicia
T
2

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.

Tiberius answered 30/5, 2019 at 22:8 Comment(1)
fully agree. also making methods return the original object seems very construed just to make the syntax sugary - returning the original object has nothing to do with the actual functionality and responsibility of the methods imo. it's shoehorning a return value for the sake of syntax, at the expense of single responsibility/concern.Thermopylae
B
1

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

Bael answered 29/9, 2017 at 14:22 Comment(0)
P
0

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.

Pirozzo answered 1/4, 2012 at 3:3 Comment(4)
Repeating a variable name in the source code has nothing to do with the DRY principle. DRY states that "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system", or in other terms, avoid the repetition of knowledge (not the repetition of text).Encarnalize
It most certainly is repetition that violates dry. Repeating variable names (unnecessarily) causes all the evil in the same ways other forms of dry do: It creates more dependencies and more work. In the above example, if we rename A, the wet version will need 3 changes and will cause errors to debug if any of the three are missed.Pirozzo
I can't see the problem you pointed out in this example, since all method calls are close to each other. Also, if you forget to change the name of a variable in one line, the compiler will return an error and this will be corrected before you can run your program. Also, the name of a variable is limited to the scope of its declaration. Unless this variable is global, which is already a bad programming practice. IMO, DRY is not about less typing, but about keeping things isolated.Encarnalize
The "compiler" may return an error, or maybe you're using PHP or JS and there interpreter may only emit an error at runtime when you hit this condition.Pirozzo
I
0

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.

Ilene answered 17/7, 2020 at 19:19 Comment(0)
F
0

Linq queries are a good example of method chaining, where a typical query looks like the following:

lstObjects
  .Where(...)
  .Select(...)
  .OrderBy(...)
  .ThenBy(...)
  .ToList();

In my opinion, this is quite intuitive and avoids unnecessary temporary variables to store partial results which one may be hardly interested in.

One subtler thing to notice is the usage of the "ThenBy" extension method, which is available to be called only after a call to an "OrderBy" or "OrderByDescending" method. That means an internal state is also maintained here which determines whether a ThenBy can be called or not. And like in this query, there are cases where the client application may not be interested in storing the internal state in a temporary variable, but may only be interested in the final results.

So, while writing a library if we want to provide an API that gives a sense of performing a certain set of operations in a certain order, then allowing method chaining would make the usage of the library much more intuitive.

Firing answered 16/10, 2022 at 17:2 Comment(0)
S
0

Well, I see one most important advantage of method chaining is You don't have to keep repeating calls to same object for doing consecutive operations. You must use it.

Streeter answered 15/3, 2023 at 11:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.