Ein Builder sollte normalerweise nur einmalig verwendet werden. Es kann aber Fälle geben, wo die Wiederverwendbarkeit ziemlich bequem ist. Hier ein Beispiel, wie es funktioniert.

<?php

class Builder {
        private $propA;
        public function withPropA($a) {
                $this->propA = $a;
                return $this;
        }
}

$builder1 = new Builder(); 
$builder1->withPropA("foo");

// this won't work as expected, since builder1 is modified before cloned
// $builder2 = clone $builder1->withPropA("bar");

// syntax error :(
// $builder2 = (clone $builder1)->withPropA("bar");

$builder2 = clone $builder1; 
$builder2->withPropA("bar");

print_r($builder1); // foo
print_r($builder2); // bar

Soweit das Beispiel, mit „einfachen“ (skalaren) Klassen Variablen (inkl. Arrays – diese werden ebenfalls kopiert). Wird jedoch ein inneres Objekt verwendet (oder ein „`&$pointer„`), muss man aufpassen. Folgendes Beispiel geht „schief“:

// als Fortsetzung zum oberen Code

class SubClass {
        public $propB;
}

$sub = new SubClass();
$sub->propB = "boo";

$builder1->withPropA($sub);
$builder3 = clone $builder1;
$sub->propB = "far"; 
// changes the state of both builder objects!!

print_r($builder1); // far!!
print_r($builder3); // far!!

Um solch einen Fall korrekt zu handhaben, kann man im Builder die „__clone“ Methode wie folgt implementieren:

class Builder2 {
        private $propA;
        public function withPropA($a){
                $this->propA = $a;
                return $this;
        }
        public function __clone() {
                if (is_object($this->propA)) {
                        $this->propA = clone $this->propA;
                }
        }
}

$builder1 = new Builder2();
$builder1->withPropA($sub);
$builder3 = clone $builder1;
$sub->propB = "yes"; 
// changes the state of $builder1 since this is the object set to builder1

print_r($builder1); // yes
print_r($builder3); // far (value before it was cloned)

Auch wenn hier jetzt das Klonen richtig funktioniert, ist der Code trotzdem etwas verwirrend, da $builder1 verändert wird, obwohl $sub erst nach dem clonen modifiziert wird. Daher sollte solcher Code aufgrund der Leserlichkeit trotzdem vermieden werden.

Trotzdem sollte das Klonen bei Objekt-Variablen auf jeden Fall wie im letzten Beispiel umgesetzt werden!

Advertisements