Design Patterns In Action: The Builder

When creating complex objects, the Builder pattern is the perfect tool to have in your design arsenal. It allows you to separate the construction process of an object from its representation, giving you more flexibility and control.

Let’s dive in and we’ll see how the Builder pattern can be used to create a cyberpunk character step by step, adding attributes such as a name, weapon, armor, and accessories. We’ll also see how the pattern can be used to keep the construction process separate from the object representation, allowing for easy modification and customization of the final product.

So, without further ado, let’s get started building our cyberpunk character and discovering the power of the Builder pattern!

class CyberpunkCharacterBuilder {
    private $character;

    public function __construct() {
        $this->character = new CyberpunkCharacter();
    }

    public function addName($name) {
        $this->character->name = $name;
        return $this;
    }

    public function addWeapon($weapon) {
        $this->character->weapon = $weapon;
        return $this;
    }

    public function addArmor($armor) {
        $this->character->armor = $armor;
        return $this;
    }

    public function addAccessories($accessories) {
        $this->character->accessories = $accessories;
        return $this;
    }

    public function build() {
        return $this->character;
    }
}

class CyberpunkCharacter {
    public $name;
    public $weapon;
    public $armor;
    public $accessories;
}

$cyberpunkBuilder = new CyberpunkCharacterBuilder();
$cyberpunk = $cyberpunkBuilder
                ->addName("Cyberpunk")
                ->addWeapon("Plasma Rifle")
                ->addArmor("Titanium Plates")
                ->addAccessories("Holographic Display")
                ->build();

echo "Meet $cyberpunk->name, a cyberpunk character armed with $cyberpunk->weapon, protected by $cyberpunk->armor and accessorized with $cyberpunk->accessories. Ready to hack the world and look cool while doing it!";

While that’s a good start, let’s see if we can clean things up a bit by coding to an interface and using a getter/setter with array scheme:

interface Character {
    public function addAttribute(string $name, $value):void;
    public function getAttribute(string $name);
}

class CyberpunkCharacter implements Character {
    private $attributes = [];

    public function addAttribute(string $name, $value):void {
        $this->attributes[$name] = $value;
    }

    public function getAttribute(string $name) {
        return $this->attributes[$name] ?? null;
    }
}

class CyberpunkCharacterPrinter {
    public function print(Character $character) {
        echo "Meet {$character->getAttribute('name')}, a cyberpunk character ready to hack the world and look cool while doing it! armed with {$character->getAttribute('weapon')} and protected by {$character->getAttribute('armor')} and accessorized with {$character->getAttribute('accessories')}. And his partner {$character->getattribute('partner')} is armed with {$character->getattribute('partner_weapon')}";
    }
}

$cyberpunk = new CyberpunkCharacter();
$cyberpunk->addAttribute("name", "Jack");
$cyberpunk->addAttribute("weapon", "Plasma Rifle");
$cyberpunk->addAttribute("armor", "Titanium Plates");
$cyberpunk->addAttribute("accessories", "Holographic Display");
$cyberpunk->addattribute("partner", "Luna");
$cyberpunk->addattribute("partner_weapon", "Smartgun");

$printer = new CyberpunkCharacterPrinter();
$printer->print($cyberpunk);

Or maybe the stdClass() is more up your alley:

class CyberpunkCharacterBuilder {
    private $character;

    public function __construct() {
        $this->character = new stdClass();
    }

    public function addAttribute($name, $value) {
        $this->character->$name = $value;
        return $this;
    }

    public function build() {
        return $this->character;
    }
}

$cyberpunkBuilder = new CyberpunkCharacterBuilder();
$cyberpunk = $cyberpunkBuilder
                ->addAttribute("name", "Jack")
                ->addAttribute("weapon", "Plasma Rifle")
                ->addattribute("armor", "Titanium Plates")
                ->addattribute("accessories", "Holographic Display")
                ->addattribute("partner", "Luna")
                ->addattribute("partner_weapon", "Smartgun")
                ->build();

echo "Meet {$cyberpunk->name}, a cyberpunk character ready to hack the world and look cool while doing it! armed with {$cyberpunk->weapon} and protected by {$cyberpunk->armor} and accessorized with {$cyberpunk->accessories} and his partner {$cyberpunk->partner} that is armed with {$cyberpunk->partner_weapon} ";

While using the stdClass can be a good choice if you need a simple and flexible way to store the different parts of an object, there are some potential cons to using it:

  • Type Safety: Unlike arrays, stdClass does not provide type safety, meaning that you can assign any type of value to a property without any error or warning. This can lead to bugs.
  • Performance: Using stdClass can have a small impact on the performance of your code, since the properties are stored as an array inside the object, which requires more memory and CPU cycles than a simple array. Probably not a big deal these days, but something to be aware of.
  • Error handling: when using stdClass you have to manually handle the case when a property doesn’t exist, unlike an array you can’t use the isset() or array_key_exists() functions to check if a property exists, you have to use the property_exists() function, which can make the code more complex.
Related Posts