This week we’ll continue our exploration of design patterns and look at the last creational pattern, the Builder pattern.
The Builder Pattern
The Gang of Four book defines the Builder pattern as follows:
“Separate the construction of a complex object from its representation so that the same construction process can create different representations.”
The Problem
We occasionally have complex objects in our system that require laborious, step-by-step initialization of many of their internal fields. The Builder pattern provides a simple process to construct such a complex object. We use it when we have a number of mandatory and optional attributes to set when constructing an object.
To initialise an object with many different attributes, we can either:
- Construct the object with one of a number of complex constructors with a variety of attribute combinations.
- Construct the object with one complex constructor containing all the possible attributes, many of which will not be needed all the time.
- Construct the object with a simple constructor and call multiple setters to initialise it.
None of these solutions is particularly elegant, and they’re all error prone, because it is easy to forget to perform some of the required steps.
The Solution: Builder Pattern
To implement the Builder pattern, we refactor the object construction steps. We remove the complex construction code from its own class, and we move it into a separate builder class. This allows us to create complex objects using chained method calls (a fluent interface).
We can build a particular configuration of an object step by step, using only those steps that we really need. We use a director object to specify the required steps in the correct order. Finally, the finished product is returned from the builder class. After implementing this pattern, we won’t need to implement complex constructors with many parameters.
Steps to implement the Builder pattern:
- Provide a constructor for the mandatory attributes of the object.
- Provide setter (and getter) methods for the optional attributes.
- Create a static nested builder class within the class. This could also be a top-level class.
- Create corresponding setters methods in the builder class which return a reference to the same builder object.
- Provide a
build()
method, which returns the constructed complex object.
Builder Pattern: Example Code
While developing our first person shooter game, we’ve hit a bit of a snag. We’ve created a Combatant
class with a list of attributes that keeps growing. Obviously a Combatant
will have a required type. This could be a Monster
, a Beast
, a Zombie
, a Human
, whatever our creative director dreams up. But combatants will also have characteristics such as strength, speed, self-healing, intelligence, etc. They will also sustain damage, carry and wield weapons, wear armour, collect and store treasure, the list goes on. Some of these attributes will be mandatory, such as the type and intelligence, but others could be optional, using good default values.
The CombatantType
could include the following values:
public enum CombatantType { HUMAN, BEAST, MONSTER, ZOMBIE; }
The Combatant
class follows:
public class Combatant { // mandatory attributes private CombatantType type; // enum type private int intelligence; // optional attributes - using int types here for ease private int strength; private int speed; private int healing; // and more - damage, weapons, armour, treasure, etc. // a complex constructor with many parameters public Combatant(CombatantType type, int intelligence, int speed, int healing, int strength) { // appropriate code } // setters for optional attributes // other Combatant code... } // end of class
Calling a complex constructor with many parameters is confusing, error prone and difficult to modify and maintain.
We could rather implement the Builder pattern by creating a static inner class inside the Combatant
class, and use that to build combatants as required.
public class Combatant { // mandatory attributes private CombatantType type; // enum type private int intelligence; // optional attributes - using int types here for ease private int strength; private int speed; private int healing; // and more - damage, weapons, armour, treasure, etc. // private constructor taking a Builder private Combatant(Builder builder) { this.type = builder.type; this.intelligence = builder.intelligence; this.strength = builder.strength; this.speed = builder.speed; this.healing = builder.healing; } // other Combatant code... @Override public String toString() { return String.format("%s [IQ=%d, strength=%d, speed=%d, healing=%d]", type.toString(), intelligence, strength, speed, healing); } // nested inner class public static class Builder { // mandatory attributes private CombatantType type; private int intelligence; // optional attributes with defaults private int strength = 10; private int speed = 10; private int healing = 0; // mandatory attributes public Builder(CombatantType type, int intelligence) { this.type = type; this.intelligence = intelligence; } // optional attributes public Builder strength(int strength) { this.strength = strength; return this; } public Builder speed(int speed) { this.speed = speed; return this; } public Builder healing(int healing) { this.healing = healing; return this; } public Combatant build() { return new Combatant(this); } } } // end of class
This will be used as follows:
public class BuilderClient { public static void main(String[] args) { Combatant zombie = new Combatant.Builder(CombatantType.ZOMBIE, 50) .speed(20) .build(); System.out.println(zombie); Combatant human = new Combatant.Builder(CombatantType.HUMAN, 120) .speed(80) .strength(80) .healing(20) .build(); System.out.println(human); } } // end of class
The builder pattern can give us a lot of flexibility in how we create objects. We can configure an object to our requirements with very little effort and in an easy to read manner.
What’s Next?
In the weeks ahead, we’ll continue examining some of the more useful design patterns. Stay tuned!
As always, please share your comments and questions.