它提供了將對(duì)象恢復(fù)到之前狀態(tài)(通過(guò)回滾撤銷)或訪問(wèn)對(duì)象狀態(tài)的能力,而不需要揭示它的實(shí)現(xiàn)(對(duì)象不需要具有返回當(dāng)前狀態(tài)的函數(shù))。
memento模式由三個(gè)對(duì)象實(shí)現(xiàn):Originator, Caretaker, Memento。
Memento – 包含任何對(duì)象或資源狀態(tài)的具體唯一快照的對(duì)象:字符串、數(shù)字、數(shù)組、類的實(shí)例等。 這種情況下的唯一性并不意味著禁止在不同的快照中存在相似的狀態(tài)。 這意味著可以將狀態(tài)提取為獨(dú)立克隆。 存儲(chǔ)在 Memento 中的任何對(duì)象都應(yīng)該是原始對(duì)象的完整副本,而不是對(duì)原始對(duì)象的引用。 Memento 對(duì)象是一個(gè)“不透明對(duì)象”(沒(méi)有人可以或不應(yīng)該更改的對(duì)象)。
Originator—它是一個(gè)包含外部對(duì)象實(shí)際狀態(tài)的對(duì)象,是嚴(yán)格指定的類型。Originator能夠創(chuàng)建此狀態(tài)的唯一副本,并將其包裹在Memento中返回。 Originator不知道變化的歷史。 您可以從外部將具體狀態(tài)設(shè)置為Originator,這將被視為實(shí)際狀態(tài)。 Originator必須確保給定的狀態(tài)對(duì)應(yīng)于允許的對(duì)象類型。Originator可能(但不應(yīng)該)有任何方法,但他們不能對(duì)保存的對(duì)象狀態(tài)進(jìn)行更改。
Caretaker控制著狀態(tài)的歷史。 他可以對(duì)一個(gè)對(duì)象進(jìn)行更改; 決定在Originator中保存外部對(duì)象的狀態(tài);從當(dāng)前狀態(tài)的Originator快照中詢問(wèn); 或?qū)riginator狀態(tài)設(shè)置為與歷史記錄中的某些快照等效。
Memento.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Memento; class Memento { public function __construct(private State $state) { } public function getState(): State { return $this->state; } }
State.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Memento; use InvalidArgumentException; class State implements \Stringable { public const STATE_CREATED = 'created'; public const STATE_OPENED = 'opened'; public const STATE_ASSIGNED = 'assigned'; public const STATE_CLOSED = 'closed'; private string $state; /** * @var string[] */ private static array $validStates = [ self::STATE_CREATED, self::STATE_OPENED, self::STATE_ASSIGNED, self::STATE_CLOSED, ]; 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 declare(strict_types=1); namespace DesignPatterns\Behavioral\Memento; /** * Ticket is the "Originator" in this implementation */ class Ticket { private State $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 declare(strict_types=1); 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(); // open the ticket $ticket->open(); $openedState = $ticket->getState(); $this->assertSame(State::STATE_OPENED, (string) $ticket->getState()); $memento = $ticket->saveToMemento(); // assign the ticket $ticket->assign(); $this->assertSame(State::STATE_ASSIGNED, (string) $ticket->getState()); // now restore to the opened state, but verify that the state object has been cloned for the memento $ticket->restoreFromMemento($memento); $this->assertSame(State::STATE_OPENED, (string) $ticket->getState()); $this->assertNotSame($openedState, $ticket->getState()); } }
更多建議: