Background

The Builder pattern is one of the “Gang of Four” (GoF) design patterns. It is classified as a creational design pattern—i.e. it’s intended to address some brittle historical practices (i.e. anti-patterns) for creating object instances.

When using the Builder pattern, we create an object of one type by creating an object of a second, supporting type, invoking a number of methods on the second object, ending with a method that returns a fully constructed object instance (often immutable) of the first type.

Scenario

Requirements

A client has asked us to develop a random password generator component. In joint design conversations, we agree on the following minimal technical and functional requirements:

  1. The generator functionality will be abstracted and encapsulated into a class; instances of the class will be created on demand, and used to generate 1 or more passwords in a single request.

  2. The generator must be configurable, to allow inclusion/exclusion of the following:

    • Upper-case letters from the Latin alphabet;
    • Lower-case letters from the Latin alphabet;
    • The digits 0-9;
    • Punctuation and (printable) symbols.
  3. Against our advice, the client insisted (and we accepted) that for each of the above character sets, the generator must be configurable with a minimum count, to enforce policies such as “A password must include at least 1 upper-case letter, 1 lower-case letter, and 1 special character.”

  4. In general, the generator should support all of the punctuation and symbol characters in the Basic Latin Unicode block, except for the space character (i.e. valid characters are in the ranges \u0021–\u002F, \u003A–\u0040, \u005B–\u0060, \u007B–\u007E). However, on exploring the contexts in which the generated passwords might be used, we agreed with the client that the generator should support some constraints on punctuation and symbols—specifically, the generator must allow, on initialization, the exclusion of a subset of punctuation and symbol characters. (More generally, we may opt to provide a mechanism for excluding any characters that would otherwise be considered valid.)

  5. The generator must allow optional exclusion of the mutually ambiguous character pairs, “1” & “l” (lower-case “L”), and “0” (zero) & upper-case “O”. This option must be enabled by default—though it has no effect if digits are excluded.

  6. The generator class should have no dependencies on external configuration files. All of the above configuration should be specifiable by the class’s consumer.

  7. At our insistence, the lifecycle of the generator object will be one-way: initialize/configure a generator instance, then use it; changing configuration options on a generator instance after we use it to generate passwords will not be supported.

Technical Specifications

Our next task is to propose an API for consuming the class—that is, for instantiating, initializing, and invoking methods on instances of the class. Very quickly, we come to the realization that, with the wide variety of configuration options, initializing the generator could become pretty complicated. In many (maybe most) use cases, we won’t need to change more than one or two of the configuration options from their default values—but in a few cases, we’ll need to change most of those options. We need to come up with an initialization approach that not only makes the generator component easy to work with for the simple use cases, but flexible enough for the tricky ones.

Approach 1: Constructor tricks

Approach 2: Using accessors and mutators (getters and setters)

Rather than write a constructor that’s complicated in its invocation—or in the implementation code required to make the invocation less complicated—we might instead write a very simple constructor, and use mutators to set the generator options. As is often the case when we use accessors and mutators, this gives us a certain level of encapsulation (generally a good thing), at the expense of boilerplate code.

(Note that the example here doesn’t make use of the PHP “magic methods” __set and __get. These methods can be very useful—though they have serious shortcomings if we have an aim of writing self-documenting code—but they’re outside the scope of this introduction.)

class Generator 
{

    private $upperIncluded;
    private $lowerIncluded;
    private $digitIncluded;
    private $punctuationIncluded;
    private $ambiguousExcluded;
    private $minUpper;
    private $minLower;
    private $minDigits;
    private $minPunctuation;
    // More fields here ...
    
    public __construct()
    {
        // General initialization code here ...
    }

    public isUpperIncluded(): boolean 
    {
        return $this->upperIncluded;
    }

    public setUpperIncluded(boolean $upperIncluded)
    {
        $this->upperIncluded = $upperIncluded;
    }

    public isLowerIncluded(): boolean 
    {
        return $this->lowerIncluded;
    }

    public setLowerIncluded(boolean $lowerIncluded)
    {
        $this->lowerIncluded = $lowerIncluded;
    }

    // More getters and setters here ...
    
    // More methods here ...

}

This certainly looks like a good approach. Among other benefits, we could include new configuration options in the future, without modifying the approach. Further, if we ever decide to load configuration options from files, there are libraries that will infer property names from a settings file, and automatically invoke the appropriate mutators. This approach is also much more self-documenting than any of the constructor-oriented options described above.

On the other hand, there’s no guarantee that after some sequence of mutator invocations, the generator is in a suitable state to begin generating passwords; ensuring that would mean making the mutators much more aware of the entire state of the generator than we’d normally like them to be. Further, there’s nothing preventing modifications of a generator instance via a mutator, even after we’ve begun using it to generate passwords. To satisfy the requirements that we and the client agreed to, the generator objects should really be immutable—that is, after instantiation and initialization by a constructor, the object state shouldn’t be allowed to change. Since the point of a mutator is to change the state of an object, this approach may be a dead end for us.

Approach 3: Constructing immutable objects with a Builder

Let’s examine a different approach altogether. Instead of one class with a complicated constructor (which we could use to create an immutable object, at the cost of code that’s difficult to document, maintain, and use), or one class with simple constructor with mutators for every configuration option (more self-documenting, but won’t produce immutable objects), we’ll implement the Builder pattern with 2 classes:

To summarize how these classes will be used: the consumer code will invoke PasswordGenerator::builder to get an instance of PasswordGeneratorBuilder; then, after setting options via methods of the latter class, the consumer will use the build method of that class to get a PasswordGenerator, and then use the generate method of that class to generate passwords.

This usage is illustrated in the following code (condensed from the accompanying generator_demo.php file).

// Build a generator with the default options.
$generator = PasswordGenerator::builder()->build();

// Generate a single password with the default length.
echo $generator->generate(), "\n";

// Build a generator with punctuation excluded from the pool.
$generator = PasswordGenerator::builder()
    ->includePunctuation(false)
    ->build();

// Generate a single password of length 16.
echo $generator->generate(16), "\n";
 
/* 
 * Build a generator with digits & punctuation excluded, requiring at least 1
 * upper- & 1 lower-case letter.
 */
$generator = PasswordGenerator::builder()
    ->includeDigit(false)
    ->includePunctuation(false)
    ->requireUpper(1)
    ->requireLower(1)
    ->build();

// Generate 10 passwords of 16 characters each.
echo print_r($generator->generate(16, 10), true), "\n";

At first glance, this usage choreography probably seems convoluted—and in fact, the Builder pattern doesn’t require that we do things exactly this way. But this aspect of the implementation lets us define both of these classes as abstract classes, with protected constructors. There will be no way for consumer code to create an instances of either of these two classes using the new keyword with a constructor; it will have to use the methods mentioned above—which is exactly what we wanted. Further, since these are both abstract classes, some of the more specialized aspects of the processing will be performed by overridden methods in subclasses. Implementing in this fashion should give us a lot of flexibility for further subclassing, as necessary (e.g. for specialized password generation requirements we haven’t anticipated yet).

Implementation

The accompanying PHP files contain the implementation of the above classes, along with an example script that demonstrates their use.

Summary

When do we use the Builder pattern?

The Builder pattern is useful in a variety of situations, including (but not limited to) any of the following:

How do we implement it?

In general, we need at least 2 abstract classes or interfaces—1 for the builder type, and one for the target object type—and at least 2 concrete classes, extending or implementing the abstract classes or interfaces. (For compactness in the scenario above, our implementing classes are anonymous classes, but it’s more common to use named classes for this purpose.) We might also include a “director” class, which invokes the necessary operations on the builder to build an instance of the target class; in many real-world cases, the consumer’s code performs this role.

In relatively simple cases, we might take advantage of some of the strengths of the Builder pattern without going to the lengths of defining abstract classes or interfaces—that is, we might simply use 2 concrete classes: 1 for the builder, and 1 for the target object. In fact, this is essentially how the Builder pattern is implemented for the Java StringBuilder and String class. Keep in mind, however, that using abstract classes or interfaces to declare the API of our builder and target types gives us more flexibility for the future.

The builder class will typically have several methods that configure the components and behavior of the (eventual) target object. (These methods are good candidates for a fluent interface, where each method returns the object instance on which the method was invoked, so that we can easily chain multiple invocations of these methods. As noted previously, however, the Builder pattern does not necessarily imply a fluent interface.) It will also have at least 1 method that constructs and returns an instance of the target object type (e.g. our PasswordGeneratorBuilder::build method). In practice, this method (and typically the entire concrete builder class) will have privileged knowledge of, and access to, the implementation details of the associated concrete target class. (In part, this is why we used anonymous classes for our scenario: by including the the concrete subclass of the target class within the builder class, the latter had privileged access to the former. In PHP, at least as of v7.1, this is the only way to nest the definition of one class within another.)

For creating a builder instance, we’ll usually provide either a constructor in the concrete builder class, or a static factory method of the abstract target class (e.g. PasswordGenerator::builder).