Make Your Code Look Good with The Builder Pattern

The Builder Pattern is the pattern of using a wrapping class to instantiate an object or objects, populate them with the correct values, and return them to the user. It offloads the responsibility of knowing how to build up each object to one location. From a DRY perspective it's great, but it really does just move the mess from one place to another.

Normally when people talk about The Builder Pattern they discuss the advantages and disadvantages of the technique and why you would use it. It's normal to start talking about getters and setters, and chainable interfaces.

For example:

class PersonBuilder {

    /** @return Person $person **/
    public function build($data) {
        return (new Person)
            ->setGender($data['gender'])
            ->setHeight($data['height']);
    }

}

class Person {

    protected $gender;
    protected $height;

    public function setGender($gender) {
        $this->gender = $gender;

        return $this;
    }

    public function setHeight($height) {
        $this->height = $height;

        return $this;
    }

    //...
}

I'm not really going to talk about that, because I don't wholly buy into the idea that you should build up an object from the outside.

I do it, mind you, but I don't feel 100% comfortable with it from an OOP perspective. Do it for style

I use builders for their original purpose (that is to populate classes with data) - usually from HTTP request data - but this isn’t the only way they can be used.

If you want elegant, expressive, natural language-like syntax in your programs, you can use The Builder Pattern.

For example, if you have an email template system that you customise on a per-recipient basis, you want to be able to have isolation between the two personalised email messages that would be generated, and you also want a lovely way of asking for them.

You can do this by wrapping separate builder classes behind methods in your service classes.

class Emailer {

  public function personalize(Template $template) {
    return new EmailPersonalizationBuilder($template);
  }

}

class EmailPersonalizationBuilder {

  /** @var Template $template **/
  protected $template;  

  public function __construct(Template $template) {
    $this->template = $template;
  }

  public function for(Recipient $recipient) {
    $body = // ... do your customization
    return (new Email)->setBody($body);
  }
}

The beauty in this method becomes apparent when you see its usage:

$template = //...
$emailer = //... get your emailer (preferably an injected service)

$recipient = new Recipient("john@example.com");

// The magic!
$email = $emailer->personalize($template)->for($recipient);
// Much better than:
//       $emailer->personalizeTemplate($template, $recipient);

// proceed to send your email!

You gain several advantages by using this:

  • Isolation between messages that are personalised.
  • A fluent, expressive syntax that reads like natural English.
  • A restricted API to make it impossible for consumers to misuse your class or package.

And that’s it!


If you liked this post, subscribe to my email list & I'll let you know when I post something new.

    Powered By ConvertKit