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());
        }
    }