Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#

Hierarchically Implementing the Bloch's Builder Pattern in C#

Rate me:
Please Sign up or sign in to vote.
4.82/5 (18 votes)
15 Aug 2011CPOL8 min read 44.1K   283   37   14
Taking advantage of C# Generics to create a parallel hierarchy of builders with less total effort.

Introduction

The Bloch's Builder Pattern involves creating a builder class for the convenient creation of a target class. A builder consists of methods that set specific properties on the target object, and returns the builder itself so that subsequent calls can be chained. An example use of a builder may be:

C#
Window window = new WindowBuilder()
    .Width(500)
    .Height(300)
    .Build();

To briefly illustrate how this is done, the WindowBuilder class has roughly the following member definitions:

C#
class WindowBuilder
{
    Window window;
    public WindowBuilder()
    {
        window = new Window();
    }
    public WindowBuilder Width(int width)
    {
        window.Width = width;
        return this;
    }
    public WindowBuilder Height(int height)
    {
        window.Height = height;
        return this;
    }
    public Window Build()
    {
        Window result = window;
        window = null; //prevent multiple invocations of Build()
        return result;
    }
}

As you see, each method returning the builder itself enables chaining.

(This article introduces a new pattern of creating builders. Because this is a technique rather than a library, I suggest that you read the whole article to grasp a thorough understanding of this concept. But of course, you can skip to the end to read the complete example code or download it, but the code might be a bit difficult to understand this way.)

Problems with this design

Suppose that the class I used above has two properties, Width and Height. Suppose now that I have a subclass of the class Window, called Dialog. The Dialog class has a new property called Message, presumably storing the message displayed by the dialog.

The problem arises when I try to create an analogous DialogBuilder. I would have to rewrite the builder methods Width() and Height(). While this doesn't sound too bad, imagine creating a builder for a WinForms component such as Button, which has many properties. We certainly don't want to write a builder method for every single property each time we have a subclass.

A Parallel Hierarchy

By the term "Parallel Hierarchy", I'm referring to a similar inheritance structure between the builders and their target classes. Specifically, if Dialog inherits from Window, then DialogBuilder shall inherit from WindowBuilder. This also suggests that DialogBuilder should take advantage of existing methods in WindowBuilder, namely Width() and Height(), so that DialogBuilder does not have to re-implement these.

The first problem arises: The return type of Width() and Height(). They return WindowBuilder. This means that after I call Width(), I will no longer be able to build a Dialog because I get a WindowBuilder in return. However, WindowBuilder has no way of knowing that it should return DialogBuilder instead.

The solution here is Generics. Let us modify WindowBuilder like the following:

C#
class WindowBuilder<T> where T : WindowBuilder<T>
{
    public T Width(int width)
    {
        return (T)this;
    }
    public T Height(int height)
    {
        return (T)this;
    }
}

Let me explain what this means. WindowBuilder now takes a type argument, T, which shall refer to the type of the actual builder we'll be using. Then the property-setting methods will return T, enabling the chaining on the actual builder. We place a restriction on T that it must be a subclass of WindowBuilder (or itself). This restriction is important because it prevents the compiler from complaining when we convert this to T upon returning. (There is potentially a downcasting performance issue here, but this should rarely matter, considering the situations where a builder would be used. But if you're concerned, we'll look into that later in this article.)

Note here that we've omitted the actual builder logic, just for clarity.

Now we'll look at the DialogBuilder to see how a builder should inherit another:

C#
class DialogBuilder<T> : WindowBuilder<T> where T : DialogBuilder<T>
{
    public T Message(string message)
    {
        return (T)this;
    }
}

Again, the builder logic code is omitted to emphasize on the inheritance pattern. Here the class definition on the first line looks very convoluted. Let me explain.

Except for the tokens ": WindowBuilder<T>", everything else is just analogous to the WindowBuilder declaration. We want DialogBuilder to be a subclass of WindowBuilder, and we pass the type parameter T to the base class to tell the base class what builder it should return. We also restrict T to be a subclass of DialogBuilder, both to enable casting in (T)this, and to ensure that this class is used correctly.

Instantiating the builder

We have arrived at an interesting issue: How are we going to instantiate the builder? I've mentioned that T refers to the type of the actual builder, but to build a Dialog, for example, what is the type of the actual builder? It can't be DialogBuilder because we lack an argument. It can't be DialogBuilder<DialogBuilder> since we still lack an argument. Alas, it seems like we need a class called DialogBuilder<DialogBuilder<DialogBuilder<...>>>, but that's impossible. So here is the workaround:

C#
class DialogBuilder : DialogBuilder<DialogBuilder>{ }

Wow wow, hold on. What is this mess? Well, the code above takes advantage of a feature in C# that DialogBuilder and DialogBuilder<T> would refer to two entirely different classes. Calling the new class DialogBuilder here is merely for neatness and to avoid namespace pollution. Notice how the new DialogBuilder class satisfies the condition on T, that is a subclass of DialogBuilder<T>! This means that we can now instantiate the builder as follows:

C#
new DialogBuilder()

This is neat because it hides from the user all the messy Generics. Next, we'll look at implementing the builder logic.

The builder logic

So far we've been focusing on the parallel inheritance of builders, but we missed an important aspect: to actually make the builder build something.

The original builder on the very top of this article created a Window object upon initialization of the builder, and modified the Window object as the user called the builder methods. I don't intend to mimic the same style here, because I would be creating a Window object and a Dialog object upon instantiation of DialogBuilder. Alternatively, I can create a BuilderBase class which contains an object referring to the actual object being built, but then every call to modify this object would involve a cast. Neither of these is very efficient.

The solution, again, is Generics.

We will add a generic argument to the builders, which indicate which class we are building. In this case, Window or Dialog. To take care of other builders we may create, let's create a BuilderBase class as follows:

C#
class BuilderBase<T, P> where T : BuilderBase<T, P> where P : class, new()
{
    protected P obj;
    protected BuilderBase()
    {
        obj = new P();
    }
    public P Build()
    {
        P result = obj;
        obj = null;
        return result;
    }
}

The added type argument P refers to the class being built. It is restricted to be a class (there is no point to support this kind of builders for structs because structs cannot inherit from each other), and it must support the default parameter-less constructor. A builder usually instantiates the target object with the default constructor, because alternate constructors should be used without a builder. So it's safe to assume that our target class supports the default constructor. The case where the target class is abstract is not a problem, because P will never be an abstract class anyway. Builders for abstract classes can indeed be implemented. (We'll soon see how in this article.)

The BuilderBase class keeps a reference to the object being built of type P. It has a Build() method that gives the object and sets it to null to prevent accidental duplicate calls of Build().

Now let's look at the new functional WindowBuilder:

C#
class WindowBuilder<T, P> : BuilderBase<T, P> 
    where T : WindowBuilder<T, P> where P : Window, new()
{
    public T Width(int width)
    {
        obj.Width = width;
        return (T)this;
    }
    public T Height(int height)
    {
        obj.Height = height;
        return (T)this;
    }
}

We've changed this class to inherit from BuilderBase<T, P>, passing P along. T is now naturally restricted to subclass WindowBuilder<T, P>, and P must be a subclass of Window and can be instantiated with the default constructor.

Despite the mess on the class declaration, the body of the class is very simple. We simply set the corresponding properties on the object, which must be a subclass of Window, then return the builder itself.

Now let's look at the DialogBuilder:

C#
class DialogBuilder<T, P> : WindowBuilder<T, P> 
      where T : DialogBuilder<T, P> where P : Dialog, new()
{
    public T Message(string message)
    {
        obj.Message = message;
        return (T)this;
    }
}

The builder class declaration is analogous every time, even for abstract classes. The distinction between abstract class builders and non-abstract class builders is that abstract class builders don't (and can't) implement the following:

C#
class WindowBuilder : WindowBuilder<WindowBuilder, Window> { }
class DialogBuilder : DialogBuilder<DialogBuilder, Dialog> { }

We now have a complete and working set of builders for the Window and Dialog classes. Example invocations are:

C#
Window window = new WindowBuilder()
    .Width(500)
    .Height(400)
    .Build();
Dialog dialog = new DialogBuilder()
    .Width(500)
    .Message("Hello")
    .Height(400)
    .Build();

Getting rid of casts

Let us revisit the statement:

C#
return (T)this;

ILDASM shows that this statement does involve the unbox.any instruction, which for a reference type is the same as a castclass instruction. This instruction can be potentially slow. For the sake of perfection, let's get rid of this cast (or at least, cast only once).

Add a reference to the BuilderBase called _this of type T, and assign to this reference inside the constructor:

C#
class BuilderBase<T, P> where T : BuilderBase<T, P> where P : class, new()
{
    protected P obj;
    protected T _this;
    protected BuilderBase()
    {
        obj = new P();
        _this = (T)this;
    }
    public P Build()
    {
        P result = obj;
        obj = _this = null;
        return result;
    }
}

This seems like the best we can do, because we cannot inherit from a type argument T, so this can never be implicitly cast to T.

Notice that I nullified _this upon Build(); perhaps this is unnecessary but it gets rid of all references to the builder, making it easier for the garbage collector. I'm not sure of this but an extra null assignment doesn't hurt. (If you know about this, please tell me about it. Thanks!)

Then we proceed to change every return statement to the following:

C#
return _this;

The complete code

Here it is, the complete code in this Window and Dialog example above:

C#
using System;
using System.Collections.Generic;
using System.Text;

namespace HPMV.Builders
{
    class TestProgram
    {
        static void Main(string[] args)
        {
            Window window = new WindowBuilder()
                .Width(500)
                .Height(400)
                .Build();
            Dialog dialog = new DialogBuilder()
                .Width(500)
                .Message("Hello")
                .Height(400)
                .Build();
        }
    }


    class BuilderBase<T, P>
        where T : BuilderBase<T, P>
        where P : class, new()
    {
        protected P obj;
        protected T _this;
        protected BuilderBase()
        {
            obj = new P();
            _this = (T)this;
        }
        public P Build()
        {
            P result = obj;
            obj = null;
            return result;
        }
    }

    class Window
    {
        public int Width { get; set; }
        public int Height { get; set; }
    }

    class Dialog : Window
    {
        public string Message { get; set; }
    }

    class WindowBuilder<T, P> : BuilderBase<T, P>
        where T : WindowBuilder<T, P>
        where P : Window, new()
    {
        public T Width(int width)
        {
            obj.Width = width;
            return _this;
        }
        public T Height(int height)
        {
            obj.Height = height;
            return _this;
        }
    }
    class WindowBuilder : WindowBuilder<WindowBuilder, Window> { }

    class DialogBuilder<T, P> : WindowBuilder<T, P>
        where T : DialogBuilder<T, P>
        where P : Dialog, new()
    {
        public T Message(string message)
        {
            obj.Message = message;
            return _this;
        }
    }
    class DialogBuilder : DialogBuilder<DialogBuilder, Dialog> { }
}

More builders

By following the pattern in the example code, we can make new builders quite easily. The only complex effort here is writing the class declaration correctly, but a few tries and you should get used to it. Of course, you may also create a snippet or something similar, but I won't go into that in this article.

I hope you have found this builder pattern interesting and useful.

Credits

The parallel hierarchy pattern was observed in a recent build of JavaFX 2.0 Beta in the javafx.builders package. Because of the difference in the way Java and C# deal with Generics, the exact technique was impossible to apply to C#. The technique in this article is original, inspired by the aforementioned JavaFX API.

History

  • August 15, 2011: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionGreat article. A question about extensions. Pin
zulu12-Aug-15 12:47
zulu12-Aug-15 12:47 
GeneralMy vote of 5 Pin
Paulo Zemek5-Aug-13 16:17
mvaPaulo Zemek5-Aug-13 16:17 
GeneralRe: My vote of 5 Pin
Robin Cheng (HPMV)5-Aug-13 17:10
Robin Cheng (HPMV)5-Aug-13 17:10 
Questioncast issue Pin
burntass11-Jan-13 15:20
burntass11-Jan-13 15:20 
AnswerRe: cast issue Pin
burntass11-Jan-13 16:46
burntass11-Jan-13 16:46 
GeneralRe: cast issue Pin
Robin Cheng (HPMV)12-Jan-13 2:20
Robin Cheng (HPMV)12-Jan-13 2:20 
QuestionThe benefit there is, indeed Pin
Aoi Karasu 9-Sep-11 8:21
professional Aoi Karasu 9-Sep-11 8:21 
Question+5 for 'brilliance' in use of generics, but ... what are the costs/benefits of this technique ? [modified] Pin
BillWoodruff22-Aug-11 15:55
professionalBillWoodruff22-Aug-11 15:55 
AnswerRe: +5 for 'brilliance' in use of generics, but ... what are the costs/benefits of this technique ? [modified] Pin
Robin Cheng (HPMV)22-Aug-11 16:41
Robin Cheng (HPMV)22-Aug-11 16:41 
Questionwhat is the benefit? Pin
Fabse_net22-Aug-11 11:10
Fabse_net22-Aug-11 11:10 
AnswerRe: what is the benefit? Pin
Robin Cheng (HPMV)22-Aug-11 16:42
Robin Cheng (HPMV)22-Aug-11 16:42 
GeneralMy vote of 5 Pin
Frederico Barbosa16-Aug-11 7:02
Frederico Barbosa16-Aug-11 7:02 
GeneralMy vote of 5 Pin
mintxelas15-Aug-11 21:00
mintxelas15-Aug-11 21:00 
GeneralRe: My vote of 5 Pin
Robin Cheng (HPMV)16-Aug-11 1:26
Robin Cheng (HPMV)16-Aug-11 1:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.