Memento (patrón de deseño)

Na Galipedia, a Wikipedia en galego.

O patrón de deseño Memento (ou Lembranza ou Token) é un patrón de comportamento que permite capturar e extraer o estado interno dun obxecto, respectando a súa encapsulación, de xeito que, posteriormente, inda que se modifique o obxecto, se poida restaurar ese estado.

Motivación[editar | editar a fonte]

Para permitir funcionalidades como desfacer unha acción ou, en caso de erro, volver a un estado consistente, é necesario gardar o estado interno dun obxecto. O patrón Memento, permite levar isto a cabo sen romper a encapsulación dos obxectos.

Por exemplo, no caso dun editor gráfico que permita conectar figuras. Dados dous rectángulos que se poden conectar mediante unha liña. O editor permite, que aínda que despracemos os rectángulos, a liña modifica a súa lonxitude co fin de que os rectángulos permanezan sempre conectados. Cando se quere volver a unha posición anterior non é tan fácil como parece. Pódese dar o caso de que ó desfacer unha acción non restauremos a posición inicial, ou que non se produza o efecto desexado. Este problema pode resolverse empregando o patrón Memento. A opción de desfacer solicitará un Recordo ó Creador cando necesite comprobar o seu estado. O Creador inicializa o memento coa información do estado actual. E só o Creador pode almacenar e recuperar a información do Recordo.

Aplicabilidade[editar | editar a fonte]

Emprégase o patrón Memento cando o estado dun obxecto debe ser almacenado de modo que poida ser recuperado e restaurado noutro momento futuro, ou ben cando unha interface para obter o estado rompa a encapsulación do obxecto expoñendo detalles da súa implementación.

Estrutura[editar | editar a fonte]

Estrutura do patrón Memento

Participantes[editar | editar a fonte]

  • Recordo (ou Memento) : É un obxecto que almacena o estado interno doutro obxecto, o Creador. Pode almacenar tanta información como fose preciso. Só permite o acceso ó estado almacenado á clase Creador, impedindo accesos doutros obxectos que non sexan este último. Poderíase dicir que o Memento ten como dúas interfaces: unha reducida, que sería aquela a que tería acceso o Conserxe e unha ampla que permite acceder ó Creador a todos os datos necesarios para restaurar o estado anterior.
  • Creador (ou Originator): É o encargado de crear un Recordo do estado interno actual do obxecto. Vai empregar ese Recordo para restaurar a dito estado.
  • Conserxe (ou Caretaker): É o responsable de almacenar de forma segura os Recordos. Nunca opera ou examina os contidos dos Recordos.

Colaboracións[editar | editar a fonte]

  • O Conserxe require un Recordo do Creador, gárdao durante un tempo e, se é necesario, devólvello ó Creador. Isto último non sempre sucederá, posto que o Creador non ten porque volver á un estado anterior. Reflíctese esta situación no diagrama de secuencia.
Diagrama de secuencia
  • Os Recordos son pasivos, é dicir, que tan só o Creador é o encargado de adxudicar ou de recuperar o seu estado.

Consecuencias[editar | editar a fonte]

  • Evita a exposición a outros obxectos de información que só debe xestionar o Creador, pero que debe de estar almacenada fóra deste. Polo tanto, o patrón protexe os límites da encapsulación.
  • Fai máis sinxelo o Creador, ó separar a xestión dos Recordos. É dicir, que o Cliente sexa o que xestione o estado que solicita simplifica ó Creador e evita que o Cliente teña que notificar ó Creador cando remata.
  • O emprego de Recordos pode resultar custoso, xa que o Creador ten que gardar no Recordo moita información, ou se producise o caso de que o Cliente crea e devolve recordos frecuentemente.
  • En ocasións, é difícil nalgúns linguaxes de programación o soporte de dúas visibilidades distintas (a interface reducida á que accedía o Conserxe, e unha ampliada, á que accedía o Creador).
  • Oculta o custo que implica almacenar o Recordo. O Conserxe que é o responsable de eliminar os Recordos debería de ser lixeiro, sen embargo, pode provocar grandes custos de almacenamento ó gardar Recordos, xa que non sabe canta información hai gardada neles.

Implementación[editar | editar a fonte]

  • Soporte da linguaxe para as dúas visibilidades diferentes.
  • Garda os cambios incrementais, é dicir que o Recordo pode gardar aqueles cambios que se producen con respecto ó estado interno do Creador. Isto é útil na implementación da operación Desfacer, dado que se coñece a secuencia de cambios realizados (o histórico de cambios). Desta forma, os mementos poden gardar só o cambio producido por unha orden e non toda á información referente ós estados ós que afecta.

Exemplo[editar | editar a fonte]

 
public class Memento { 
 
    Memento() { _estado = new java.util.Hashtable(); } 
 
    void garda(String attr, Object valor) { _estado.put(attr, valor); } 
 
    Object recupera(String attr, Object defecto) { 
        return _estado.containsKey(attr) ? _estado.get(attr) : defecto; 
    } 
    private java.util.Hashtable _estado; 
}
 
 
public class ObxectoComplexo {     
 
    public Memento crearMemento() { 
        Memento estadoActual = new Memento(); 
        estadoActual.guarda(NOME, _nome); 
        estadoActual.guarda(UNENTEIRO, new Integer(_unEnteiro)); 
        estadoActual.guarda(COLECCIÓN, _colección.clone()); 
        return estadoActual; 
    } 
 
    public void restaurar(Memento previo) { 
        _nombre = (String) previo.recupera(NOME, _nome); 
        _colección = (java.util.Vector) previo.recupera(COLECCIÓN, _coleccion); 
        Integer i = (Integer) previo.recupera(UNENTEIRO, new Integer(_unEnteiro)); 
        _unEnteiro = i.intValue(); 
    } 
 
    public Memento cambiarNome(String nome) { 
        Memento recordo = new Memento(); 
        recordo.guarda(NOME, _nome); 
        _nome = nome; 
        return recordo; 
    } 
 
    // máis métodos que modifican o estado do obxecto... 
 
    private String _nome; 
    private static final String NOME = "exemplo.ObxectoComplexo._nome"; 
    private int _unEnteiro; 
    private static final String UNENTEIRO = "exemplo.ObxectoComplexo._unEnteiro"; 
    private java.util.Vector _colección = new java.util.Vector(); 
    private static final String COLECCIÓN = "exemplo.ObxectoComplexo._colección"; 
  }


Véxase tamén[editar | editar a fonte]

Bibliografía[editar | editar a fonte]

  • E. Gamma, R. Helm, R. Johnson and J. Vlissides (1995). Design Patterns: elements of reusable object-oriented software.