Хранитель (англ. Memento) — поведенческий шаблон проектирования, позволяющий, не нарушая инкапсуляцию, зафиксировать
и сохранить внутреннее состояние объекта так, чтобы позднее восстановить его в это состояние.
Шаблон Хранитель мы реализуем тремя объектами: Player
(Создателем), Game
(Опекуном) и Save
(Хранитель).
Хранитель - это объект, который хранит конкретный снимок состояния некоторого объекта или ресурса: строки, числа, массива, экземпляра класса и так далее. Уникальность в данном случае подразумевает не запрет на существование одинаковых состояний в разных снимках, а то, что состояние можно извлечь в виде независимой копии. Любой объект, сохраняемый в Хранителе, должен быть полной копией исходного объекта, а не ссылкой на исходный объект. Сам объект Хранитель является «непрозрачным объектом» (тот, который никто не может и не должен изменять).
Создатель — это объект, который содержит в себе актуальное состояние внешнего объекта строго заданного типа и умеет создавать уникальную копию этого состояния, возвращая её, обёрнутую в объект Хранителя. Создатель не знает истории изменений. Создателю можно принудительно установить конкретное состояние извне, которое будет считаться актуальным. Создатель должен позаботиться о том, чтобы это состояние соответствовало типу объекта, с которым ему разрешено работать. Создатель может (но не обязан) иметь любые методы, но они не могут менять сохранённое состояние объекта.
Опекун управляет историей снимков состояний. Он может вносить изменения в объект, принимать решение о сохранении состояния внешнего объекта в Создателе, запрашивать от Создателя снимок текущего состояния, или привести состояние Создателя в соответствие с состоянием какого-то снимка из истории.
-
Game.php
<?php namespace DesignPatterns\Behavioral\Memento; /** * Class Game * @package DesignPatterns\Behavioral\Memento */ class Game { /** * @var Save */ protected $save; /** * @param Player $player */ public function makeSave(Player $player) { $this->save = $player->getMemento(); } /** * @param Player $player */ public function makeLoad(Player $player) { $player->setMemento($this->save); } }
-
Player.php
<?php namespace DesignPatterns\Behavioral\Memento; /** * Class Player * @package DesignPatterns\Behavioral\Memento */ class Player { const DIE_HP_COLOR = 'GREY'; const LOW_HP_COLOR = 'RED'; const MID_HP_COLOR = 'YELLOW'; const HIGHT_HP_COLOR = 'GREEN'; /** * @var int */ protected $hp; /** * Player constructor. */ public function __construct() { $this->hp = 100; } /** * @param int $hp * * @return void */ public function getDamage(int $hp) { $this->hp -= $hp; } /** * @param int $hp * * @return void */ public function getHeals(int $hp) { $this->hp += $hp; } /** * @return string */ public function getColorHealthPointBar() { if ($this->hp > 70) { return self::HIGHT_HP_COLOR; } if ($this->hp <= 70 && $this->hp > 40) { return self::MID_HP_COLOR; } if ($this->hp <= 40 && $this->hp > 0) { return self::LOW_HP_COLOR; } return self::DIE_HP_COLOR; } /** * @param Save $memento * * @return void */ public function setMemento(Save $memento) { $this->hp = $memento->getState(); } /** * @return Save */ public function getMemento() { return new Save($this->hp); } }
-
Save.php
<?php namespace DesignPatterns\Behavioral\Memento; /** * Class Save * @package DesignPatterns\Behavioral\Memento */ class Save { /** * @var int */ protected $hp; /** * Save constructor. * * @param int $hp */ public function __construct(int $hp) { $this->hp = $hp; } /** * @return int */ public function getState() { return $this->hp; } }
Test case:
MementoTest.php
<?php
namespace DesignPatterns\Tests\Behavioral\Memento;
use DesignPatterns\Behavioral\Memento\Game;
use DesignPatterns\Behavioral\Memento\Player;
use PHPUnit_Framework_TestCase;
class MementoTest extends PHPUnit_Framework_TestCase
{
/**
* @var Game
*/
protected static $game;
/**
* @var Player
*/
protected static $player;
public static function setUpBeforeClass(): void
{
self::$game = new Game();
self::$player = new Player();
}
public function testPlayerGetDamage()
{
$this->assertEquals(Player::HIGHT_HP_COLOR, self::$player->getColorHealthPointBar());
self::$player->getDamage(30);
$this->assertEquals(Player::MID_HP_COLOR, self::$player->getColorHealthPointBar());
}
public function testPlayerGetHeals()
{
$this->assertEquals(Player::MID_HP_COLOR, self::$player->getColorHealthPointBar());
self::$player->getHeals(10);
$this->assertEquals(Player::HIGHT_HP_COLOR, self::$player->getColorHealthPointBar());
}
public function testPlayerSaveAndLoad()
{
$this->assertEquals(Player::HIGHT_HP_COLOR, self::$player->getColorHealthPointBar());
self::$game->makeSave(self::$player);
self::$player->getDamage(30);
$this->assertEquals(Player::MID_HP_COLOR, self::$player->getColorHealthPointBar());
self::$player->getDamage(30);
$this->assertEquals(Player::LOW_HP_COLOR, self::$player->getColorHealthPointBar());
self::$game->makeLoad(self::$player);
$this->assertEquals(Player::HIGHT_HP_COLOR, self::$player->getColorHealthPointBar());
}
}