Padrão de memento - Memento pattern

O padrão memento é um padrão de design de software que fornece a capacidade de restaurar um objeto ao seu estado anterior ( desfazer por meio de reversão).

O padrão memento é implementado com três objetos: o originador , um zelador e um memento . O originador é algum objeto que possui um estado interno. O zelador vai fazer algo para o originador, mas deseja desfazer a alteração. O zelador primeiro pede ao originador um objeto memento. Em seguida, ele faz qualquer operação (ou sequência de operações) que iria fazer. Para retornar ao estado anterior às operações, ele retorna o objeto memento ao originador. O próprio objeto memento é um objeto opaco (aquele que o zelador não pode, ou não deve, mudar). Ao usar esse padrão, deve-se tomar cuidado se o originador pode alterar outros objetos ou recursos - o padrão memento opera em um único objeto.

Exemplos clássicos do padrão memento incluem um gerador de número pseudo - aleatório (cada consumidor do PRNG serve como um zelador que pode inicializar o PRNG (o originador) com a mesma semente (o memento) para produzir uma sequência idêntica de números pseudo-aleatórios) e o estado em uma máquina de estados finitos .

Visão geral

O padrão de design Memento é um dos vinte e três padrões de design GoF bem conhecidos que descrevem como resolver problemas de design recorrentes para projetar software orientado a objetos flexível e reutilizável, ou seja, objetos que são mais fáceis de implementar, alterar, testar e reuso. O Memento Pattern foi criado por Noah Thompson, David Espiritu e Dr. Drew Clinkenbeard para os primeiros produtos HP.

Que problemas o padrão de design Memento pode resolver?

  • O estado interno de um objeto deve ser salvo externamente para que o objeto possa ser restaurado a este estado posteriormente.
  • O encapsulamento do objeto não deve ser violado.

O problema é que um objeto bem projetado é encapsulado de forma que sua representação (estrutura de dados) fica oculta dentro do objeto e não pode ser acessada de fora do objeto.

Que solução o padrão de design Memento descreve?

Tornar um objeto (originador) responsável por

  • salvar seu estado interno para um objeto (memento) e
  • restaurar a um estado anterior a partir de um objeto (memento).

Apenas o originador que criou um memento tem permissão para acessá-lo.

Um cliente (zelador) pode solicitar um memento do originador (para salvar o estado interno do originador) e passar um memento de volta para o originador (para restaurar a um estado anterior).

Isso permite salvar e restaurar o estado interno de um originador sem violar seu encapsulamento.

Consulte também a classe UML e o diagrama de sequência abaixo.

Estrutura

Classe UML e diagrama de sequência

Image
Um exemplo de classe UML e diagrama de sequência para o padrão de design Memento.

No diagrama de classes UML acima , a Caretakerclasse se refere à Originatorclasse para salvar ( createMemento()) e restaurar ( restore(memento)) o estado interno do originador.
A Originatorclasse implementa
(1) createMemento()criando e retornando um Mementoobjeto que armazena o estado interno atual do originador e
(2) restore(memento)restaurando o estado do Mementoobjeto passado .

O diagrama de sequência UML mostra as interações em tempo de execução:
(1) Salvando o estado interno do originador: O Caretakerobjeto chama createMemento()o Originatorobjeto, que cria um Mementoobjeto, salva seu estado interno atual ( setState()) e retorna o Mementopara o Caretaker.
(2) Restaurando o estado interno do originador: As Caretakerchamadas restore(memento)no Originatorobjeto e especifica o Mementoobjeto que armazena o estado que deve ser restaurado. O Originatorobtém o estado ( getState()) de Mementopara definir seu próprio estado.

Exemplo de Java

O programa Java a seguir ilustra o uso de "desfazer" do padrão memento.

import java.util.List;
import java.util.ArrayList;
class Originator {
    private String state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento..
 
    public void set(String state) {
        this.state = state;
        System.out.println("Originator: Setting state to " + state);
    }
 
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(this.state);
    }
 
    public void restoreFromMemento(Memento memento) {
        this.state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }
 
    public static class Memento {
        private final String state;

        public Memento(String stateToSave) {
            state = stateToSave;
        }
 
        // accessible by outer class only
        private String getSavedState() {
            return state;
        }
    }
}
 
class Caretaker {
    public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
 
        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.add(originator.saveToMemento());
        originator.set("State4");
 
        originator.restoreFromMemento(savedStates.get(1));   
    }
}

O resultado é:

Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3

Este exemplo usa uma String como o estado, que é um objeto imutável em Java. Em cenários da vida real, o estado quase sempre será um objeto mutável, caso em que uma cópia do estado deve ser feita.

Deve-se dizer que a implementação mostrada tem uma desvantagem: ela declara uma classe interna. Seria melhor se esta estratégia de memento pudesse se aplicar a mais de um originador.

Existem basicamente três outras maneiras de obter Memento:

  1. Serialização.
  2. Uma classe declarada no mesmo pacote.
  3. O objeto também pode ser acessado por meio de um proxy, que pode realizar qualquer operação de salvar / restaurar no objeto.

Exemplo C #

O padrão memento permite capturar o estado interno de um objeto sem violar o encapsulamento, de forma que mais tarde se possa desfazer / reverter as alterações, se necessário. Aqui, pode-se ver que o objeto memento é realmente usado para reverter as alterações feitas no objeto.

class Memento
{
    private readonly string savedState;

    private Memento(string stateToSave)
    {
        savedState = stateToSave;
    }

    public class Originator
    {
        private string state;
        // The class could also contain additional data that is not part of the
        // state saved in the memento.

        public void Set(string state)
        {
            Console.WriteLine("Originator: Setting state to " + state);
            this.state = state;
        }

        public Memento SaveToMemento()
        {
            Console.WriteLine("Originator: Saving to Memento.");
            return new Memento(state);
        }

        public void RestoreFromMemento(Memento memento)
        {
            state = memento.savedState;
            Console.WriteLine("Originator: State after restoring from Memento: " + state);
        }
    }
}

class Caretaker
{
    static void Main(string[] args)
    {
        List<Memento> savedStates = new List<Memento>();

        Memento.Originator originator = new Memento.Originator();
        originator.Set("State1");
        originator.Set("State2");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State4");

        originator.RestoreFromMemento(savedStates[1]);
    }
}

Exemplo Python

"""
Memento pattern example.
"""


class Memento:
    def __init__(self, state) -> None:
        self._state = state

    def get_saved_state(self):
        return self._state


class Originator:
    _state = ""

    def set(self, state) -> None:
        print("Originator: Setting state to", state)
        self._state = state

    def save_to_memento(self) -> Memento:
        print("Originator: Saving to Memento.")
        return Memento(self._state)

    def restore_from_memento(self, memento) -> None:
        self._state = memento.get_saved_state()
        print("Originator: State after restoring from Memento:", self._state)


saved_states = []
originator = Originator()
originator.set("State1")
originator.set("State2")
saved_states.append(originator.save_to_memento())

originator.set("State3")
saved_states.append(originator.save_to_memento())

originator.set("State4")

originator.restore_from_memento(saved_states[1])

Referências

links externos