Why Would I Not Enable Chaining?
This is mostly a matter of preference. Using chaining tends to create more readable code, and can actually reduce CPU/heap memory. For example, consider these two snippets of code:
// No chaining
KeyPair kp = new KeyPair();
kp.setKey(key);
kp.setValue(value);
Utils.someMethod(kp);
// Chaining
Utils.someMethod(new KeyPair().setKey(key).setValue(value));
With chaining, we've turned four lines of code into just 1, and removed a temporary variable that was only used to hold data long enough to set the values. I commonly use this pattern to build custom exceptions that can throw additional information:
throw new CustomException().withValue(value);
This pattern is more frequently found with classes that have a ton of optional parameters where only a few will be set. This can drastically reduce the number of times a single function has to be written. For example, imagine you had a class like this:
class SomeClass {
SomeClass(Object value1) {
...
}
SomeClass(Object value1, Object value2) {
...
}
SomeClass(Object value1, Object value2, Object value3) {
...
}
...
SomeClass(Object value1, Object value2, Object value3, Object value4, Object value5, Object value6, Object value7, Object value8, Object value9, Object value10...) {
...
}
}
When there's a ton of optional parameters available, mistakes tend to be made. A function that accepts 32 parameters, many of them optional, can be a nightmare to manage. For example, consider these two pieces of code:
Utils.someFunction(null, null, null, null, null, null, null, null, 5, null, null, null, null, null, null, 2, null, null, 'Hello World');
Utils.someFunction(new Parameter().setParam9(5).setParam16(2).setParam19('Hello World'));
Which one do you think is easier to read? Which is easier to maintain? Not only do you know what values you're assigning, you also get the option to order them any way you want to:
Utils.someFunction(new Parameter().setParam16(2).setParam9(5).setParam19('Hello World'));
This can reduce the SomeClass
complexity, and large functions that can have an arbitrarily large number of parameters can be reduced to a far more readable form. This also essentially lets us have named parameters, much like Ruby, C#, and other advanced languages.
However, you shouldn't use this everywhere. Use chaining when it makes sense, but don't return values that will never be used. This only clutters your code and makes it more confusing in the long run, and does marginally increase CPU usage (it's faster to not return a value at all than it is to return a chainable object).
As @KeithC pointed out by way of the link (Builder Pattern: Good for code, great for tests), this also lets you build objects that have a ton of default values but can then be customized. The code mentioned there is Java, but also easily applies to Apex Code as well. In fact, I'll borrow one of their examples to demonstrate:
Account account = new AccountBuilder()
.withId(1)
.withName("test")
.withBalance(10)
.build();
Using builders lets you set a bunch of default values (maybe record types, required fields, etc), as well as providing customization. In fact, this AccountBuilder
class could easily be a legitimate piece of Apex Code. You could easily build records for unit tests with default values, and customize each account to a specific unit test. Of course, these exact functions may not make sense in your organization's case, but it demonstrates how you could build standard records with default values and optional customizations.
I'll play devil's advocate with some reasons you might not want to chain methods. (I'm not necessarily saying a fluent interface is a bad thing, just providing some counter points.)
Debugging and error handling
Utils.someFunction(new Parameter().setParam9(someVariable).setParam16(anotherVariable).setParam19(weDontHardCodeParametersWhereIComeFrom));
Quick, which part of that line just threw a System.NullPointerException
? If you are lucky you might get an accurate column reference. More likely you will only have a line number.
If you could, say, set an actual breakpoint, how would you indicate which part of that line you wanted to stop on? You would either need to set the breakpoint within the method of interest, or painfully step through/over each chained method until you got to the one you want.
Last time I checked, the code coverage functionality in Salesforce only worked by the line rather than the statement. It wouldn't be possible to distinguish what is happening in that line.
Potentially Long lines of code
So there are 32 optional parameters. What happens on the day when someone needs to set all of them?!
Utils.someFunction(new Parameter().setParam1(5).setParam2(2).setParam3('Hello World').setParam4('Help').setParam5(6).setParam6('Make').setParam7('the').setParam8('maddness').setParam9('Stop!').setParam10('It').setParam11('Hurts!').setParam12('My').setParam13('eyes!').setParam14('Hello World').setParam15('Hello World').setParam16('Hello World').setParam17('Hello World').setParam18('Hello World').setParam19('Hello World').setParam20('Hello World').setParam21('Hello World').setParam22('Hello World').setParam23('Hello World').setParam24('Hello World').setParam25('Hello World').setParam26('Hello World').setParam27('Hello World').setParam28('Hello World').setParam29('Hello World').setParam30('Hello World').setParam31('Hello World').setParam32('Sob') );
Or if you split one method per line:
Utils.someFunction(
new Parameter()
.setParam1(5)
.setParam2(2)
.setParam3('Hello World')
.setParam4('Help')
.setParam5(6)
.setParam6('Make')
.setParam7('the')
.setParam8('madness')
.setParam8('stop!')
...
.setParam32('Sob') );
Now we have one apex code statement spanning 30+ lines. This comes back to the first point about debugging and error handling.
Also, you can't put comments in for individual method calls right next to the method. As per discussion with sfdcfox in the SFSE comments, it is possible to have Apex comments mid statement.
Do you have an object in a consistent/valid state?
If you only provide constructors for an object that will create the instance in a consistent state then it will be much harder for others (or your future self) to muck it up. After the constructor completes, the object will be ready to use; no further initialization is required to set optional properties.
Whereas, with a chained factory, how do you know which methods you need to call to get a valid Account?
Account account = new AccountBuilder()
.build();
BoomException: Account can't build as it doesn't have Id, Name or Amount set!
Chaining should be restricted to the original object type
You don't want to chain from one object type to another. It would break the ability to chain further expected methods and it isn't apparent when the object type changes.
See also: Method chaining - why is it a good practice, or not?
Some quotes from there:
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. Source
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. Source
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. Source - Martin Fowler