PHP設計模式之備忘錄模式(Memento)代碼實例大全(25)

目的

它提供了在不破壞封裝(對象不需要具有返回當前狀態的函數)的情況下恢復到之前狀態(使用回滾)或者獲取對象的內部狀態。

備忘錄模式使用 3 個類來實現:Originator,Caretaker 和 Memento。

Memento —— 負責存儲 Originator 的 唯一內部狀態 ,它可以包含: string,number, array,類的實例等等。Memento 「不是公開的類」(任何人都不應該且不能更改它),並防止 Originator 以外的對象訪問它,它提供 2 個接口:Caretaker 只能看到備忘錄的窄接口,他只能將備忘錄傳遞給其他對象。Originator 卻可看到備忘錄的寬接口,允許它訪問返回到先前狀態所需要的所有數據。

Originator —— 它負責創建 Memento ,並記錄 外部對象當前時刻的狀態, 並可使用 Memento 恢復內部狀態。Originator 可根據需要決定 Memento 存儲 Originator 的哪些內部狀態。 Originator 也許(不是應該)有自己的方法(methods)。 但是,他們 不能更改已保存對象的當前狀態。

Caretaker —— 負責保存 Memento。 它可以修改一個對象;決定 Originator 中對象當前時刻的狀態; 從 Originator 獲取對象的當前狀態; 或者回滾 Originator 中對象的狀態。

例子

  • 保存之前控制 ORM Model 中的狀態

  • 並將這個隨機數存在時序機中

  • 發送一個隨機數

UML圖

★官方PHP高級學習交流社羣「點擊」管理整理了一些資料,BAT等一線大廠進階知識體系備好(相關學習資料以及筆面試題)以及不限於:分佈式架構、高可擴展、高性能、高併發、服務器性能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階乾貨

代碼

  • Memento.php

<?php

namespace DesignPatterns\Behavioral\Memento;

class Memento
{
    /**
     * @var State
     */
    private $state;

    /**
     * @param State $stateToSave
     */
    public function __construct(State $stateToSave)
    {
        $this->state = $stateToSave;
    }

    /**
     * @return State
     */
    public function getState()
    {
        return $this->state;
    }
}
  • State.php

<?php

namespace DesignPatterns\Behavioral\Memento;

class State
{
    const STATE_CREATED = 'created';
    const STATE_OPENED = 'opened';
    const STATE_ASSIGNED = 'assigned';
    const STATE_CLOSED = 'closed';

    /**
     * @var string
     */
    private $state;

    /**
     * @var string[]
     */
    private static $validStates = [
        self::STATE_CREATED,
        self::STATE_OPENED,
        self::STATE_ASSIGNED,
        self::STATE_CLOSED,
    ];

    /**
     * @param string $state
     */
    public function __construct(string $state)
    {
        self::ensureIsValidState($state);

        $this->state = $state;
    }

    private static function ensureIsValidState(string $state)
    {
        if (!in_array($state, self::$validStates)) {
            throw new \InvalidArgumentException('Invalid state given');
        }
    }

    public function __toString(): string
    {
        return $this->state;
    }
}
  • Ticket.php

<?php

namespace DesignPatterns\Behavioral\Memento;

/**
 * Ticket 是  Originator  的原始副本
 */
class Ticket
{
    /**
     * @var State
     */
    private $currentState;

    public function __construct()
    {
        $this->currentState = new State(State::STATE_CREATED);
    }

    public function open()
    {
        $this->currentState = new State(State::STATE_OPENED);
    }

    public function assign()
    {
        $this->currentState = new State(State::STATE_ASSIGNED);
    }

    public function close()
    {
        $this->currentState = new State(State::STATE_CLOSED);
    }

    public function saveToMemento(): Memento
    {
        return new Memento(clone $this->currentState);
    }

    public function restoreFromMemento(Memento $memento)
    {
        $this->currentState = $memento->getState();
    }

    public function getState(): State
    {
        return $this->currentState;
    }
}

測試

  • Tests/MementoTest.php

<?php

namespace DesignPatterns\Behavioral\Memento\Tests;

use DesignPatterns\Behavioral\Memento\State;
use DesignPatterns\Behavioral\Memento\Ticket;
use PHPUnit\Framework\TestCase;

class MementoTest extends TestCase
{
    public function testOpenTicketAssignAndSetBackToOpen()
    {
        $ticket = new Ticket();

        // 打開 ticket
        $ticket->open();
        $openedState = $ticket->getState();
        $this->assertEquals(State::STATE_OPENED, (string) $ticket->getState());

        $memento = $ticket->saveToMemento();

        // 分配 ticket
        $ticket->assign();
        $this->assertEquals(State::STATE_ASSIGNED, (string) $ticket->getState());

        // 現在已經恢復到已打開的狀態,但需要驗證是否已經克隆了當前狀態作爲副本
        $ticket->restoreFromMemento($memento);

        $this->assertEquals(State::STATE_OPENED, (string) $ticket->getState());
        $this->assertNotSame($openedState, $ticket->getState());
    }
}

PHP 互聯網架構師成長之路*「設計模式」終極指南

PHP 互聯網架構師 50K 成長指南+行業問題解決總綱(持續更新)

面試10家公司,收穫9個offer,2020年PHP 面試問題

★如果喜歡我的文章,想與更多資深開發者一起交流學習的話,獲取更多大廠面試相關技術諮詢和指導,歡迎加入我們的羣啊,暗號:phpzh(君羊號碼856460874)。

2020年最新PHP進階教程,全系列!

內容不錯的話希望大家支持鼓勵下點個贊/喜歡,歡迎一起來交流;另外如果有什麼問題 建議 想看的內容可以在評論提出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章