3.6. Memento (Lembrança)¶
3.6.1. Objetivo¶
Ele provê a habilidade de restaurar um objeto para seu estado anterior (desfazer via rollback) ou ganhar acesso ao estado do objeto sem revelar sua implementação (p.e. o objeto não é obrigado a ter uma funcionalidade para retornar ao estado atual).
O padrão Memento é implementado com três objetos: o Originator, um Caretaker e um Memento.
Memento - um objeto que contém um snapshot único e concreto do estado de qualquer objeto ou recurso: string, número, array, uma instance de classe e assim por diante. A singularidade, neste caso, não implica a proibição da existência de estados semelhantes em diferentes snapshots. Isso significa que o estado pode ser extraído como o clone independente. Qualquer objeto armazenado no Memento deve ser uma cópia completa do objeto original em vez de uma referência para o objeto original. O objeto Memento é um “objeto opaco” (o objeto que ninguém pode ou deve mudar).
Originator - é um objeto que contém o estado atual de um objeto externo é estritamente o tipo especificado. Originator é capaz de criar uma cópia única deste estado e devolvê-lo envolto em um Memento. O O Originator não conhece a história das mudanças. Você pode definir um estado concreto ao Originator do lado de fora, que será considerado como atual. O Originator deve certificar-se de que determinado estado corresponde ao tipo permitido de objeto. Originator pode (mas não deve) ter quaisquer métodos, mas eles não podem fazer alterações no estado do objeto salvo.
Caretaker controla a história dos estados. Ele pode fazer alterações em um objeto; tomar uma decisão para salvar o estado de um objeto externo no Originator; pedir a partir do snapshot do Originator do estado atual ou definir o estado do Originator para equivalência com algum snapshot do histórico.
3.6.2. Exemplos¶
- A semente de um gerador de números pseudo-aleatórios
- O estado em uma máquina de estados finitos
- Controle para estados intermediários de ORM Model antes de salvar
3.6.3. Diagrama UML¶

3.6.4. Código¶
Você também pode encontrar este código no GitHub
Memento.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?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;
}
}
|
3.6.5. Teste¶
Tests/MementoTest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?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());
}
}
|