НЕ МОЛЧИ!!!    Сделай что-нибудь, чтобы остановить войну России в Украине.
...бойтесь людей равнодушных - именно с их молчаливого согласия происходят все самые ужасные преступления на свете.   ("Репортаж с петлёй на шее")

Шаблон проектирования Prototype (Прототип)

Представлен шаблон проектирования Прототип или Prototype, приводится описание шаблона и пример реализации на PHP.

Порождающий шаблон проектирования Прототип используется как альтернатива созданию объекта через ключевое слово new. Когда это оправдано? Например, если объект имеет множество полей, проще будет создать его копию и переназначить значения необходимых, вместо того, чтобы заново инициализировать все свойства нового объекта. Или, когда нет желания строить иерархии фабрик, параллельные иерархии классов продуктов, как это делается в шаблоне Фабричный метод.

Описание шаблона Прототип

Основная идея этого шаблона в том, чтобы создавать копии уже готовых объектов с помощью самих объектов копирования. Естественно последние должны уметь это делать, обычно это достигается введением в копируемый объект метода clone(), который производит все действия, необходимые для создания копии объекта. Объекты, умеющие делать клонирование, называют прототипами, отсюда и название паттерна.

Чтобы не привязываться к конкретным классам в Прототипе реализуется создание общего абстрактного класса, в котором объявляется публичный метод clone(). Объекты с возможностью клонирования должны быть наследниками такого класса, и обязаны реализовать метод clone().

Реализация шаблона Прототип

Ниже приведен пример одной из возможных реализаций шаблона на PHP. Реализация этого шаблона в этом случае упрощается, т.к. в PHP скриптах клонирование обеспечивается на уровне языка ключевым словом clone. Но важно помнить, что подобная операция копирует и все внутренние ссылки копируемого объекта. Получится, что прототип и его копия будут иметь общие поля, если они являются объектами, что редко востребовано. Поэтому для полностью независимой копии после создания клона надо вручную производить клонирование и внутренних объектов. Это производится с помощью магического метода __clone(), который автоматически вызывается при использовании ключевого слова clone. Ниже в примере  для того, чтобы показать, как это происходит в прототип введено объектное свойство $review.

Пример шаблона Прототип на PHP

Итак, имеем следующий скрипт index.php.

<!DOCTYPE html>

<html>
<head>
  <title>Пример реализации шаблона Прототип на PHP</title>
<style>
.bold {
  font-weight: bold;
}  
</style>
</head>

<body>

<?php
//класс краткого описания товара, который мы будем использовать,
//как свойство прототипа
class Review 
{
  private $descr = 'is absent';
  public function setReview($txt)
  {
    $this->descr = $txt;
  }
  public function getReview()
  {
    return $this->descr;
  }
}

abstract class Prototype 
{
    public function clone() {}
}

class Book extends Prototype
{
    private $name;
    private $price;
    private $category;
    public $review;
    
    public function __construct($name, $price, $category)
    {
      $this->name = $name;
      $this->price = $price;
      $this-> category = $category;
    }
    
    public function setName($name)
    {
      $this->name = $name;
    }

    public function getName()
    {
      return $this->name;
    }    

    public function setPrice($price)
    {
      $this->price = $price;
    }

    public function getPrice()
    {
      return $this->price;
    }
    
    public function setReview(Review $review)
    {
      $this->review = $review;
    }

//создаем копию внутреннего объекта review прототипа 
//и инициализируем его новым значением
    public function __clone()
    {
      $this->review = clone $this->review;
      $this->review->setReview('описание отсутствует'); //значение по умолчанию
    }

    public function getInfo()
    {
      return 'Product - <span class="bold">' .$this->getName() .'</span>, 
      cost = <span class="bold">' .$this->getPrice() .'</span>, Краткое описание: <span class="bold">' .$this->review->getReview() .'</span>';
    }
}

//тестируем созданный код
$review = new Review();    //считаем, что где-то получили описание товара
$book1 = new Book('Book1', '$8', $books);
$book1->setReview($review);

$book2 = clone $book1;
$book2->setName('Book2');
$book2->setPrice('$10');
/*вместо строки по умолчанию можно изменить значение объектного свойства $review
$book2->review->setReview('Эта книга описывает шаблоны программирования.');
*/

echo $book1->getInfo();
echo '<br>' .$book2->getInfo();
?>

</body>
</html>

После запроса скрипта index.php в браузере получим следующий отклик.

Сравниваем два наименования в категории "Книги"
Product - Book1, cost = $8, Краткое описание: is absent
Product - Book2, cost = $10, Краткое описание: описание отсутствует

Как видите, мы создали копию объекта, инициализировали ее и получили для приложения новый объект.

Перейти к списку шаблонов проектирования.