Зберігач (англ. 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());
}
}