它提供了將對象恢復到之前狀態(tài)(通過回滾撤銷)或訪問對象狀態(tài)的能力,而不需要揭示它的實現(xiàn)(對象不需要具有返回當前狀態(tài)的函數(shù))。
memento模式由三個對象實現(xiàn):Originator, Caretaker, Memento。
Memento – 包含任何對象或資源狀態(tài)的具體唯一快照的對象:字符串、數(shù)字、數(shù)組、類的實例等。 這種情況下的唯一性并不意味著禁止在不同的快照中存在相似的狀態(tài)。 這意味著可以將狀態(tài)提取為獨立克隆。 存儲在 Memento 中的任何對象都應該是原始對象的完整副本,而不是對原始對象的引用。 Memento 對象是一個“不透明對象”(沒有人可以或不應該更改的對象)。
Originator—它是一個包含外部對象實際狀態(tài)的對象,是嚴格指定的類型。Originator能夠創(chuàng)建此狀態(tài)的唯一副本,并將其包裹在Memento中返回。 Originator不知道變化的歷史。 您可以從外部將具體狀態(tài)設置為Originator,這將被視為實際狀態(tài)。 Originator必須確保給定的狀態(tài)對應于允許的對象類型。Originator可能(但不應該)有任何方法,但他們不能對保存的對象狀態(tài)進行更改。
Caretaker控制著狀態(tài)的歷史。 他可以對一個對象進行更改; 決定在Originator中保存外部對象的狀態(tài);從當前狀態(tài)的Originator快照中詢問; 或將Originator狀態(tài)設置為與歷史記錄中的某些快照等效。
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()); } }
更多建議: