Internal DSL Pattern: Method Chaining


This is a portion of a larger set of posts on the subject of Internal DSLs.

Method Chaining is a pattern explained by Martin Fowler as part of his DSL book work in progress. Fowler’s take on Method Chaining is useful to more fully understand this post (and probably realize some of this post’s flaws).

Method Chaining

Fowler defines method chaining as:

“Make modifier methods return the host object so that multiple modifiers can be invoked in a single expression.”

This approach is fairly common in all APIs. An easy example is the System.DateTime API in .NET which allows you to do things like: DateTime.Today.AddHours(4).  DateTime doesn’t exactly fit Fowler’s definition since DateTime is an immutable object following the Value Object pattern.  The outward appearance, however, is very much like method chaining and is perhaps most familiar to the widest audience of reader.

Depending on what type of DSL you’re building, method chaining takes different shape and serves different purposes.

First, Some Examples

Model-based, Generative Example

In this circumstance, method chaining would involve acting upon the semantic model instead of returning new instances of a value object as in the case of DateTime.

For this example, imagine that we’re building an internal DSL to build up various HTML elements. In this case, we’re building the portion that deals with input type=text elements (textboxes) and we want to make it a password textbox instead (type=password). Our host class is of type TextBoxExpression and we’re defining the PasswordMode() method.

public class TextBoxExpression
{
    //...

    public TextBoxExpression PasswordMode()
    {
        _element.IsPasswordType = true;
        return this;
    }
}

Model-based, Non-Generative Example

In this circumstance, method chaining would act upon the semantic model in some way by execute some action or assert some condition.

For this example, imagine that we’re building a DSL for an external model that allows functionality like pretty-printing our entities.

public class PersonFormatter
{
    public PersonFormatter(Person person, TextWriter writer)
    {
        _person = person;
        _writer = writer;
    }

    public PersonFormatter WriteDisplayName()
    {
        _writer.Write("{0}, {1}", 
            _person.LastName,
            _person.FirstName);

        return this;
    }

    public PersonFormatter WithAddress()
    {
        _writer.Write("{0} {1}, {2} {3}",
            _person.Address1,
            _person.City,
            _person.StateOrProvince,
            _person.PostalCode);

        return this;
    }
}

// example
_formatter.WriteDisplayName().WithAddress();

Key Points

The key points to know with method chaining are:

  1. Manipulating or acting upon the underlying model (_element in the first example, _person in the second example) .
  2. Returning the host object itself (TextBoxExpression, PersonFormatter) from the method to allow repeated calls to the same object.

Context Variables

In order to achieve point #1, your  host object will have to maintain a reference a to the model in its internal state. Fowler calls this a “Context Variable.”  In our examples above, these are represented by _element and _person. Context variables do not have to only be of the model type, they can be other objects including intrinsic types (int, string, etc).

Returning the same host object reference

One of the benefits of method chaining is to keep up the flow of the DSL and allow for greater discoverability (i.e. “Where do I go next?”).  Returning the object from calls upon itself allows for repeated calls to each act upon the host object (and underlying model) as a series of related commands.

Other Topics

The use of method chaining is often tightly related to the Expression Builder pattern such that any discussion of one is incomplete without discussion of the other.  There are several other complex topics around method chaining (such as progressive interfaces, depth navigation, etc).  Method chaining is a relatively simple subject. When used during the process of building a DSL, however, it can lead to complex situations requiring complex solutions. In my experience, these problems are most commonly encountered in conjunction with the Expression Builder pattern, and not often without.  Therefore, I believe these other, more complex subjects are best covered under the topic of Expression Builder.

Internal DSL Pattern: Expression Builder