Wzorzec Prototyp polega na klonowaniu instancji obiektów przy użyciu wbudowanej funkcji PHP – clone. Z tego wzorca korzystamy wtedy, kiedy potrzebujemy wielu identycznych obiektów o zbieżnych cechach i/lub właściwościach.
Schemat klonowania instancji obiektu:
1 2 |
$object = new ClassName(); $new_object = clone $object; |
Prosty przykład, to gra Pac-Man, którą każdy zna od zygoty. W grze występują złe „duszki”, które mają za zadanie dorwać Pac-Mana zanim ten wszama wszystkie „kropki” na planszy. Każdy z duszków ma identyczny kształt, funkcjonalności; „duszki” różnią się jedynie kolorem. Możemy więc zastosować wzorzec Prototyp do powielania postaci w grze.
PRZYKŁAD
W poniższym przykładzie zastosujemy wzorzec Prototyp do kopiowania różnych kształtów grafiki wektorowej SVG, np: koło (<circle></circle>) i prostokąt (<rect></rect>).
W tym celu tworzymy klasę abstrakcyjną Shape (Kształt), w której zapiszemy wspólne cechy dla wszystkich obrazów SVG, tj.: $svg_width, $svg_height – szerokość i wysokość płótna grafiki, $color – kolor figury oraz dwie metody abstrakcyjne: getShape() – zwracająca kształt i __clone() – metoda klonująca obiekt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php // Shape.php abstract class Shape { public $svg_width; public $svg_height; public $color; abstract function getShape(); abstract function __clone(); } ?> |
Poniższa klasa Circle rozszerza (extends) abstrakcyjną klasę Shape. Konstruktor __construct() przyjmuje i ustawia zmienne z klasy bazowej. Dodatkowo deklarujemy kolejne zmienne charakterystyczne dla obiektu Circle: $cx, $cy – współrzędne położenia figury na płótnie oraz $r – promień koła. Funkcja getShape() – wyświetla grafikę wektorową z zadanymi parametrami w znacznikach <svg … ><circle … /></svg>.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php // Circle.php include_once('Shape.php'); class Circle extends Shape { public $cx; public $cy; public $r; private $svg; public function __construct() { $this->svg_width = 100; $this->svg_height = 100; $this->color = 'green'; $this->cx = 50; $this->cy = 50; $this->r = 35; } public function getShape() { $this->svg.= '<svg width="'.$this->svg_width.'" height="'.$this->svg_height.'">'; $this->svg.= '<circle cx="'.$this->cx.'" cy="'.$this->cy.'" r="'.$this->r.'" fill="'.$this->color.'" />'; $this->svg.= '</svg>'; echo $this->svg; } function __clone() {} } ?> |
Analogicznie postępujemy z klasą Rectangle. Konstruktor przyjmuje oprócz domyślnych wartości, dodatkowe parametry: $width, $height – szerokość i wysokość prostokąta. Funkcja getShape() wyświetla prostokąt z podstawionymi wartościami w znacznikach <svg … ><rect … /></svg>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php // Rectangle.php include_once('Shape.php'); class Rectangle extends Shape { public $width; public $height; private $svg; public function __construct() { $this->svg_width = 100; $this->svg_height = 100; $this->color = 'blue'; $this->width = 100; $this->height = 100; } public function getShape() { $this->svg.= '<svg width="'.$this->svg_width.'" height="'.$this->svg_height.'">'; $this->svg.= '<rect width="'.$this->width.'" height="'.$this->height.'" fill="'.$this->color.'" />'; $this->svg.= '</svg>'; echo $this->svg; } function __clone() {} } ?> |
Funkcja __clone() w obu przypadkach pozwoli na użycie klonowania instancji obiektu w taki oto sposób:
1 |
$object = clone $object; |
Ważne: podczas klonowania instancji obiektu konstruktor nie jest wywoływany / uruchamiany, a więc można jedynie korzystać z jego domyślnych wartości, ale nie można wprowadzać żadnych akcji, zdarzeń, np: echo.
Na koniec tworzymy klasę Client, w której użyjemy klas potomnych do prezentacji figur:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php // Client.php include_once('Circle.php'); include_once('Rectangle.php'); // koło z domyślnymi parametrami $circle = new Circle(); $circle->getShape(); // prostokąt z domyślnymi parametrami $rectangle = new Rectangle(); $rectangle->getShape(); // przykłady z użyciem klonowania: $circle = new Circle(); $new_circle = clone $circle; $new_circle->color = 'red'; $new_circle->r = 15; $new_circle->getShape(); $rectangle = new Rectangle(); $new_rectangle = clone $rectangle; $new_rectangle->color = 'yellow'; $new_rectangle->svg_width = 200; $new_rectangle->svg_height = 100; $new_rectangle->width = 200; $new_rectangle->height = 100; $new_rectangle->getShape(); ?> |
Jak widać poszczególne prototypy możemy modyfikować, albo tworzyć klony z innych klonów 🙂