Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

The Apex Design Patterns is an awesome resource, and I learned many tricks from it. One of my favorites is method chaining, and I quickly developed an axiom:

Any time you would return void, return this instead.

It's dead simple to implement, just change:

public void myMethod()
{
    // do stuff
}

to:

public MyClass myMethod()
{
    // do stuff
    return this;
}

Does this strategy cause a performance hit? Are there there other risks/concerns that give reason to avoid it?

share|improve this question
1  
And the very first Design Pattern is the Simpleton Pattern.‌​.. – Christoffer Hammarström Mar 22 at 11:47
up vote 22 down vote accepted

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.

share|improve this answer
    
Hilarious example of parameterization. You have captured many of the use cases I dig it out for. It just seems borderline irresponsible to not return this for some of the system methods out there, for example List.addAll. – Adrian Larson Mar 21 at 19:20
    
Worth mentioning: Builder Pattern: Good for code, great for tests. – Keith C Mar 21 at 19:21
    
@KeithC Thanks. I'll add a few more edits. – sfdcfox Mar 21 at 19:26
    
@AdrianLarson I'd be the first person so jump for joy if all those functions were suddenly useful. In fact, if we had the ability to write template classes, I'd have given up on the system collections a long time ago. – sfdcfox Mar 21 at 19:53
2  
+1 for the Builder Pattern example which is much better than the way I've been setting up test objects using a home-grown descriptor approach. – cropredy Mar 21 at 22:36

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

share|improve this answer
    
One nitpick, you can multi-line the chaining. – Adrian Larson Mar 21 at 22:25
    
@AdrianLarson Do you mean split the chain? Do several calls on one line, then several on the next? – Daniel Ballinger Mar 21 at 22:26
1  
Hard to comment but basically new MyClass()\n\t.someMethod(); – Adrian Larson Mar 21 at 22:30
    
@DanielBallinger One method call per line (see sfdcfox's AccountBuilder example) is a good way format in my view. – Keith C Mar 21 at 22:31
2  
@DanielBallinger You need spaces, too. Here's an example I wrote in my dev org: new A().b() /* multiline comment */ .c().d() // EOL comment...♪ .e(); – sfdcfox Mar 21 at 23:09

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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