目的
它提供了在不破壞封裝(對象不需要具有返回當前狀態的函數)的情況下恢復到之前狀態(使用回滾)或者獲取對象的內部狀態。
備忘錄模式使用 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圖
代碼
- 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 互聯網架構師 50K 成長指南+行業問題解決總綱(持續更新)
面試10家公司,收穫9個offer,2020年PHP 面試問題
★如果喜歡我的文章,想與更多資深開發者一起交流學習的話,獲取更多大廠面試相關技術諮詢和指導,歡迎加入我們的羣啊,暗號:phpzh(君羊號碼856460874)。
內容不錯的話希望大家支持鼓勵下點個贊/喜歡,歡迎一起來交流;另外如果有什麼問題 建議 想看的內容可以在評論提出