Представлен шаблон проектирования Prototype или Прототип, приводится описание шаблона, его применение и пример реализации на PHP.
Содержание
- Применение
- Описание шаблона
- Реализация
- Итоги
Применение
Структурный шаблон проектирования Prototype используется как альтернатива созданию объектов через ключевое слово new. Когда это оправдано? Например, если объект имеет множество полей, проще будет создать его копию и переназначить значения необходимых, вместо того, чтобы заново инициализировать все свойства нового объекта. Или, когда нет желания строить иерархии фабрик, параллельные иерархии классов продуктов, как это делается в шаблоне Фабричный метод.
Описание шаблона
Основная идея шаблона в том, чтобы создавать копии уже готовых объектов с помощью самих объектов копирования. Естественно последние должны уметь это делать, обычно это достигается введением в копируемый объект метода clone(), который производит все действия, необходимые для создания копии объекта. Объекты, умеющие делать клонирование, называют прототипами, отсюда и название паттерна.
Чтобы не привязываться к конкретным классам в Прототипе реализуется создание общего абстрактного класса, в котором объявляется публичный метод clone(). Объекты с возможностью клонирования должны быть наследниками такого класса.
Реализация
Пример использования шаблона реализован на PHP. В этом случае создание шаблона упрощается, т.к. в PHP клонирование обеспечивается на уровне языка ключевым словом clone. Но важно помнить, что подобная операция копирует и все внутренние ссылки копируемого объекта. Получается, что прототип и его копия будут иметь общие поля, если они являются объектами, что редко востребовано. Поэтому для полностью независимой копии после создания клона надо вручную производить клонирование и внутренних объектов. Это производится с помощью магического метода __clone(), который автоматически вызывается при использовании ключевого слова clone. В примере для того, чтобы показать, как это происходит, в прототип введено объектное свойство $review.
Начнем с создания класса, описывающего товар.
<?php
class Review
{
private string $descr = 'описание отсутствует';
public function setReview(string $txt): void
{
$this->descr = $txt;
}
public function getReview(): string
{
return $this->descr;
}
}
Создадим абстрактный класс, который будет родителем для всех прототипируемых наследников.
abstract class Prototype
{
abstract public function __clone();
}
Создадим наследника абстрактного класса. Он умеет клонировать себя, умеет работать с описанием товара на основе класса Review.
class Book extends Prototype
{
private string $name;
private float $price;
private string $category;
private ?Review $review = null;
public function __construct(
string $name,
float $price,
string $category
) {
$this->name = $name;
$this->price = $price;
$this->category = $category;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
public function setPrice(float $price): void
{
$this->price = $price;
}
public function getPrice(): float
{
return $this->price;
}
public function setReview(Review $review): void
{
$this->review = $review;
}
public function getReview(): ?Review
{
return $this->review;
}
/**
* При клонировании книги создаём независимую копию Review.
*/
public function __clone()
{
if ($this->review !== null) {
$this->review = clone $this->review;
$this->review->setReview('описание отсутствует');
}
}
public function getInfo(): string
{
$review = $this->review?->getReview() ?? 'описание отсутствует';
return sprintf(
'Product - <span class="bold">%s</span>, cost = <span class="bold">%.2f</span>, Краткое описание: <span class="bold">%s</span>',
htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8'),
$this->price,
htmlspecialchars($review, ENT_QUOTES, 'UTF-8')
);
}
}
Всё готово для демонстрации шаблона Prototype. Вот код файла index.php, который это проделает.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Пример реализации шаблона Прототип на PHP</title>
<style>
.bold {
font-weight: bold;
}
</style>
</head>
<body>
<?php
$review = new Review();
$book1 = new Book('Book1', 8.00, 'Книги');
$book1->setReview($review);
// Создаём клон книги.
// Review также будет склонирован благодаря __clone().
$book2 = clone $book1;
// Изменяем данные клона.
$book2->setName('Book2');
$book2->setPrice(10.00);
// Меняем описание только у клона.
$book2->getReview()?->setReview(
'Эта книга описывает шаблоны программирования.'
);
echo '<span class="bold">';
echo 'Сравниваем два наименования в категории "Книги"';
echo '</span><br>';
echo $book1->getInfo();
echo '<br>';
echo $book2->getInfo();
?>
</body>
</html>
После запроса скрипта index.php в браузере получим следующий отклик.
Сравниваем два наименования в категории "Книги"
Product - Book1, cost = $8, Краткое описание: is absent
Product - Book2, cost = $10, Краткое описание: описание отсутствует
Как видите, мы создали копию объекта, инициализировали ее и получили для приложения новый объект.
Итоги
Итак, вам был представлен порождающий шаблон проектирования Prototype (Прототип), позволяющий создавать новые объекты путём копирования уже существующего объекта-прототипа. На приведенном примере была продемонстрирована работа шаблона.
В итоге хочу указать на преимущества и возможные недостатки шаблона.
Достоинства Prototype.
- Если объект требует много действий для создания и настройки, проще скопировать уже подготовленный объект и откорректировать нужное.
- Позволяет создавать объекты динамически.
- Удобно использовать для объектов со сложной начальной конфигурацией.
- Уменьшает количество наследников.
Недостатки Прототипа могут сделать применение шаблона не всегда оправданным.
- Клонирование сложных объектов может быть непростым.
- Можно случайно получить нежелательные общие объекты.
- Клонирование не всегда очевидно. Выражение в примере
$book2 = clone $book1;не объясняет, было произведено глубокое копирование(реализуется вручную через магический метод__clone()) или поверхностное(copy = clone orig;).
Остается добавить, что выбор создания объекта через new или clone за вами и определяется, как правило, контекстом.
На этом пока всё.
Успехов в разработке!
Перейти к списку шаблонов проектирования.