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