Прототип (Prototype) — шаблон проектирования
19 января 2020
Данный шаблон представляет механизм для массового создания различных объектов с помощью класса-прототипа (который содержит общие свойства и методы объектов) и последующего копирования однотипных объектов, вместо их создания с нуля.
Паттерн позволяет в клиентском коде не заниматься подготовкой целевого объекта к использованию или же снизить объём подготовки, нежели чем это было бы при создании объекта с помощью стандартной языковой директивы конструктора new (PHP, C#) или метода сreate (Delphi).
Далее приводится реализация паттерна на языке PHP. Скачать исходники (ZIP, 3 Кб)
В конце скрипта с помощью метода getInfo выводится информация об уровне жизни и силе удара для копии Рыцаря, Лучника, Гражданского и Шпиона.
Результат выполнения скрипта (идентичен результату test_copy.php):
Паттерн позволяет в клиентском коде не заниматься подготовкой целевого объекта к использованию или же снизить объём подготовки, нежели чем это было бы при создании объекта с помощью стандартной языковой директивы конструктора new (PHP, C#) или метода сreate (Delphi).
Пример задачи: создание армии юнитов на игровом поле военной стратегии.
Далее приводится реализация паттерна на языке PHP. Скачать исходники (ZIP, 3 Кб)
army_units.php
Содержит классы юнитов:- ArmyUnitPrototype – абстрактный класс, который представляет собой прототип игрового человечка (юнита). В прототипе объявлены и определены общие для всех игровых юнитов свойства и методы. Таким образом, прототип служит для удобного разделения конечных юнитов по видам, где каждый вид содержит свои настройки (например, по уровню жизни и силе удара).
Так же в данном классе определён метод getCopy, который сначала создаёт пустой объект конечного класса, а затем наполняет его свойствами. Обратите внимание на фрагмент new static(), использующий позднее статическое связывание при создании объекта; - KnightUnit – класс юнита «Рыцарь», являющийся наследником прототипа (абстрактного класса ArmyUnitPrototype);
- ArcherUnit – класс юнита «Лучник», наследник прототипа;
- CitizenUnit – класс юнита «Гражданский», наследник прототипа.
<?php
/**
* Абстрактный класс армейского юнита
*/
abstract class ArmyUnitPrototype
{
/**
* Название юнита
* @var string
*/
protected $name;
/**
* Уровень жизни
* @var int
*/
protected $lifeLevel;
/**
* Сила удара (мощность выстрела)
* @var int
*/
protected $shotPower;
/**
* Создаёт и возвращает копию текущего объекта
* @return ArmyUnitPrototype
*/
public function getCopy()
{
/*
Создаём новый объект. Обратите внимание на использование конструкции static,
которая используется для позднего статического связывания.
Подробнее: https://www.php.net/manual/ru/language.oop5.late-static-bindings.php
*/
$newUnit = new static();
//Получаем свойства текущего объекта и прописываем их значения в новый объект
$arProps = get_object_vars($this);
foreach ($arProps as $prop => $value) {
$newUnit->$prop = $value;
}
//Возвращаем новый объект
return $newUnit;
}
/**
* Устанавливает название юнита
* @param string $name - название юнита
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Устанавливает уровень жизни юнита
* @param int $value - значение силы удара
*/
public function setLifeLevel($value)
{
$this->lifeLevel = (int)$value;
}
/**
* Устанавливает силу удара юнита
* @param int $value - значение силы удара
*/
public function setShotPower($value)
{
$this->shotPower = (int)$value;
}
/**
* Возвращает строку со сведениями о юните (имя, уровень жизни и силу удара)
* @return string
*/
public function getInfo()
{
return $this->name.' | жизнь = '.$this->lifeLevel.' | удар = '.$this->shotPower;
}
}
/**
* Класс юнита "Рыцарь"
*/
class KnightUnit extends ArmyUnitPrototype
{
public function __construct()
{
$this->name = 'Рыцарь';
$this->lifeLevel = 200;
$this->shotPower = 50;
}
}
/**
* Класс юнита "Лучник"
*/
class ArcherUnit extends ArmyUnitPrototype
{
public function __construct()
{
$this->name = 'Лучник';
$this->lifeLevel = 120;
$this->shotPower = 30;
}
}
/**
* Класс юнита "Гражданский"
*/
class CitizenUnit extends ArmyUnitPrototype
{
public function __construct()
{
$this->name = 'Гражданский';
$this->lifeLevel = 100;
$this->shotPower = 10;
}
}
?>
test_copy.php
Содержит тестовый скрипт, в котором создаются юниты трёх видов: Рыцарь, Лучник и Гражданский. Затем у гражданских юнитов прокачивается навык «Самооборона», который повышает их жизнь и силу удара. Далее с помощью метода getCopy создаётся копия Рыцаря, Лучника и Гражданского. Потом копированием создаётся особый тип юнита «Шпион».<?php
//Подключение классов юнитов
require_once(__DIR__.'/army_units.php');
//Рыцарь
$knight = new KnightUnit();
//Лучник
$archer = new ArcherUnit();
//Гражданский
$citizen = new CitizenUnit();
/*
У гражданских прокачан навык "Самооборона", который повышает
уровень жизни и силу удара
*/
$citizen->setLifeLevel(115);
$citizen->setShotPower(20);
//Создаём копии юнитов, используя метод getCopy
$knight_copy = $knight->getCopy();
$archer_copy = $archer->getCopy();
$citizen_copy = $citizen->getCopy();
//Некоторые гражданские являются шпионами, у них уровень жизни выше гражданского
$citizen_spy = $citizen->getCopy();
$citizen_spy->setName('Шпион');
$citizen_spy->setLifeLevel(140);
echo $knight_copy->getInfo()."<br>";
echo $archer_copy->getInfo()."<br>";
echo $citizen_copy->getInfo()."<br>";
echo $citizen_spy->getInfo()."<br>";
?>
В конце скрипта с помощью метода getInfo выводится информация об уровне жизни и силе удара для копии Рыцаря, Лучника, Гражданского и Шпиона.
Рыцарь | жизнь = 200 | удар = 50
Лучник | жизнь = 120 | удар = 30
Гражданский | жизнь = 115 | удар = 20
Шпион | жизнь = 140 | удар = 20
test_clone.php
Содержит код, идентичный содержимому скрипта test_copy.php. Единственное различие, что для копирования объектов вместо метода getCopy используется стандартный для PHP оператор клонирования объектов clone.<?php
//Подключение классов юнитов
require_once(__DIR__.'/army_units.php');
//Рыцарь
$knight = new KnightUnit();
//Лучник
$archer = new ArcherUnit();
//Гражданский
$citizen = new CitizenUnit();
/*
У гражданских прокачан навык "Самооборона", который повышает
уровень жизни и силу удара
*/
$citizen->setLifeLevel(115);
$citizen->setShotPower(20);
//Создаём копии юнитов, используя стандартный оператор клонирования объекта clone
$knight_copy = clone $knight;
$archer_copy = clone $archer;
$citizen_copy = clone $citizen;
//Некоторые гражданские являются шпионами, у них уровень жизни выше гражданского
$citizen_spy = clone $citizen;
$citizen_spy->setName('Шпион');
$citizen_spy->setLifeLevel(140);
echo $knight_copy->getInfo()."<br>";
echo $archer_copy->getInfo()."<br>";
echo $citizen_copy->getInfo()."<br>";
echo $citizen_spy->getInfo()."<br>";
?>
Результат выполнения скрипта (идентичен результату test_copy.php):
Рыцарь | жизнь = 200 | удар = 50
Лучник | жизнь = 120 | удар = 30
Гражданский | жизнь = 115 | удар = 20
Шпион | жизнь = 140 | удар = 20