samedi 3 novembre 2018

Builder pattern implementation correctness

I'm relatively new to OOP design and would really appreciate your help in making sure I'm getting the Builder pattern right.

I'm writing a piece of code that has to create multiple objects of class A, each of them storing multiple objects of class B, each of them storing multiple objects of class C.

Obj. A1
    Obj. B1
        Obj. C1
        Obj. C2
    Obj. B2
        Obj. C3

Obj. A2
    Obj. B3
        Obj. C4
        Obj. C5
    Obj. B4
        Obj. C6
        Obj. C7
        Obj. C8

Objects of class C need to be passed to objects of class B during their instantiation. Same goes for objects of class B and A. All classes also require some additional properties.

This means that creating an A object is a rather complex multistep process, that I'd like to abstract from the client. To do this I want to use the Builder pattern.

The way I envision it, there would be 3 builder classes:

ABuilder,
BBuilder,
CBuilder,

each of them responsible for creating an object of class, respectively, A, B and C.

Then there would be also an ABuildDirector, responsible for coordinating work of the builders and delivering the final product (of class A) based on given specifications.

Builders will be injected to the Director in a dependency container on its instantiation. Then it will provide a public method "build", taking as argument an array of specs with the details for creating all the objects of classes A, B and C (those specs will be taken from a JSON file decoded as an array).

Here's how it looks in simplified PHP:

Client

// Create a $dependencies container with ABuilder, BBuilder and CBuilder
// Decode the JSON file into $specs array

$aList = [];

$aBuildDirector = new ABuildDirector($dependencies);

foreach ($specs as $spec) {
    $aList[] = aBuildDirector->build($spec);
}

ABuildDirector

class ABuildDirector
{

    protected $dependencies;

    public function __construct(Container $dependencies)
    {
        $this->dependencies = $dependencies;
    }

    public function build(array $spec) // will be composed of multiple methods in real implementation, flattened here for simplicity
    {
        $aBuilder = $this->dependencies['ABuilder']; // instantiates a new builder for each A object
        $bObjects = [];

        foreach ($spec['bObjects'] as $bObject) {

            $bBuilder = $this->dependencies['BBuilder']; 
            $cObjects = [];

            foreach ($bObject['cObjects'] as $cObject) {
                $cBuilder = $this->dependencies['CBuilder'];
                $cObjects[] = $cBuilder->setCObjProperty1($cObject['property1'])
                                       ->setCObjProperty2($cObject['property2'])
                                       ->build();
            }

            $bObjects[] = $bBuilder->setBObjProperty1($bObject['property1'])
                                   ->setBObjProperty2($bObject['property2'])
                                   ->setCs($cObjects)
                                   ->build();
         }

         return $aBuilder->setAObjProperty1($spec['property1'])
                         ->setAObjProperty2($spec['property2'])
                         ->setBs($bObjects)
                         ->build();
     }

}        

My question is, is the way of doing that described above correct from the pattern's perspective? I've read interpretations of the pattern that differed quite a bit from each other, but also none of them worked exactly as my implementation.

What differs here most from the examples I've seen is:

  • separating builders injection from passing the final product's specs to the director (one happens on director's construction, second when calling the "build" method),
  • getting the final product from the director instead of from the builder

Aucun commentaire:

Enregistrer un commentaire