OOP Design Patterns: The Builder

The Builder Pattern is rarely something you need. Often a Factory Method does the job just fine, as far as creating object instances goes. But sometimes, creating an object in a valid state would require a Create method with many parameters, and that gets annoying.

There’s something rather elegant about chained member calls that build an object. The methods of a FooBuilder class return Me, so the calling code can chain the member calls and build the object in a single, expressive statement:

    Set pizza = builder _
        .OfSize(Medium) _
        .CrustType = Classic _
        .WithPepperoni _
        .WithCheese(Mozza) _
        .WithPeppers _
        .WithMushrooms _
        .Build

The Build method returns the product, i.e. the resulting object.

So a basic (and rather flawed) builder class might look like this:

Private result As Pizza

Private Sub Class_Initialize()
Set result = New Pizza
End Sub

Public Function OfSize(ByVal sz As PizzaSize) As PizzaBuilder
If result.Size = Unspecified Then
result.Size = sz
Else
Err.Raise 5, TypeName(Me), "Size was already specified"
End If
Set OfSize = Me
End Function

Public Function WithPepperoni() As PizzaBuilder
result.Toppings.Add(Pepperoni)
Set WithPepperoni = Me
End Function

'...

Public Function Build() As IPizza
Set Build = result
End Function

Every “builder method” is a Function that returns Me, and may or may not include a bit of logic to keep the result valid. Then the Build function returns the encapsulated and incrementally initialized result object.

If the return type of the Build function is an interface (that the result object implements), then the calling code can treat all pizzas equally (assuming, say, ClassicCrustPizzaPanPizza, ThinCrustPizza are different acceptable implementations of the IPizza interface… this is where the pizza example really crumbles), and the interface can very well not expose any Property Let members.

Considerations

The builder pattern is fun and very good to know, but it’s very rarely something that’s needed. But for these times when you do need it, there are a number of things to keep in mind:

  • No temporal coupling: the order in which the calling code calls the builder methods should make no difference.
  • Builder methods may not be invoked: if a pizza without a Size isn’t a valid Pizza object, then there shouldn’t be a builder method for it; either provide sensible defaults, or make a parameterized factory that creates the builder with all the non-optional values initialized.
  • Repeated invocations: the calling code might, intentionally or not, invoke a builder method more than once. This should be handled gracefully.
  • Readability: if the fluent API of a builder isn’t making the code any easier to read, then it’s probably not worth it.

You’ll think of using a builder pattern when a factory method starts having so many parameters that the call sites are getting hard to follow: a builder can make these call sites easier to read/digest.

This SoftwareEngineering.SE answer describes the actual GoF Builder Pattern (see Design Patterns: Elements of Reusable Object-Oriented Software), which takes it a notch further and makes the builder itself abstract, using a much better example than pizza. I warmly encourage you to read it; even though the code isn’t VBA, the principles are the very same regardless.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s