Padrão de Projeto Memento Daltro Simões Gama 11/09/2013 11/09/2013 @LES/PUC-Rio 2 Objetivo v Considere um objeto, no qual desejamos preservar seu estado; v Estados do objeto em questão são mutáveis; v Queremos poder guardar “backups” dos estados do objeto, para poder restaurá-lo sob demanda. v Padrão Memento também é conhecido como “Token”. 11/09/2013 @LES/PUC-Rio 3 Motivação v Podemos pensar em várias formas de se guardar um “snapshot” de um objeto para podermos fazer “undo” em operações feitas sobre ele; § O estado de um objeto pode conter detalhes internos do objeto, que não deveriam ser expostos. v O padrão Memento traz uma forma de se implementar esta funcionalidade preservando o princípio do encapsulamento. 11/09/2013 @LES/PUC-Rio 4 Participantes v Memento: O estado propriamente dito; v Originator: O objeto que possui o estado; v Caretaker: Quem manipula o objeto. v O Caretaker fará algo no Originator, mas precisa ser capaz de desfazer as alterações, se necessário. 11/09/2013 @LES/PUC-Rio 5 Estrutura Memento Caretaker getState() setState() state Originator this.state = m.getState() setMemento(Memento m) createMemento() state return new Memento(state) 11/09/2013 @LES/PUC-Rio 6 Colaborações umCaretaker createMemento() umOriginator umMemento new Memento setState() setMemento() 11/09/2013 getState() @LES/PUC-Rio 7 Um código (JAVA) exemplo public class Originator { public static class Memento { Este é um esqueleto do memento do Originator, porém sem funcionalidade. private Object mementoState; Vamos inserir alguma funcionalidade nele. private Object getState() { return mementoState; } private void setState(Object argState) { this.mementoState = argState; } } private Object originatorState; public void setMemento(Memento argMemento){ this.originatorState = argMemento.getState(); } public Memento createMemento(){ Memento result = new Memento(); result.setState(originatorState); return result; } } 11/09/2013 @LES/PUC-Rio 8 Um código (JAVA) exemplo public class Originator { (…) private Object originatorState; public void setMemento(Memento argMemento){ this.originatorState = argMemento.getState(); } public Memento createMemento(){ Memento result = new Memento(); result.setState(originatorState); return result; } Supondo que o Originator manipula o valor public String getText(){ de um campo texto qualquer. return originatorState.toString(); } … ou melhor, o Originator é o próprio campo texto. public void setText(String newText){ originatorState = newText; } } 11/09/2013 @LES/PUC-Rio 9 Um código (JAVA) exemplo public class Originator { (…) private String originatorState; public void setMemento(Memento argMemento){ this.originatorState = argMemento.getState(); } public Memento createMemento(){ Memento result = new Memento(); result.setState(originatorState); Neste caso, o estado interno é uma String. return result; A especialização também vale pro código } da classe interna Memento. public String getText(){ return originatorState; } public void setText(String newText){ originatorState = newText; } } 11/09/2013 @LES/PUC-Rio 10 Um código (JAVA) exemplo public class Caretaker { private final Stack<Originator.Memento> savedStates = new Stack<Originator.Memento>(); private final Originator myOriginator; public Caretaker(Originator myOriginator) { this.myOriginator = myOriginator; } public void saveState(){ Originator.Memento theMemento = myOriginator.createMemento(); savedStates.push(theMemento); } public void undo(){ Originator.Memento theMemento = savedStates.pop(); myOriginator.setMemento(theMemento); } } O Caretaker se encarregará de dar uma interface de mais alto nível para o mecanismo de undo. 11/09/2013 @LES/PUC-Rio 11 Um código (JAVA) exemplo Agora um código que aciona tudo isso. public static void main(String[] args) { Originator demoOriginator = new Originator(); Caretaker demo = new Caretaker(demoOriginator); demoOriginator.setText("Olá."); demoOriginator.setText("Olá, sou o Memento"); demo.saveState(); // CheckPoint. System.out.println(demoOriginator.getText()); demoOriginator.setText("Olá, sou o Memento. Vamos brincar?"); demo.saveState(); // CheckPoint. System.out.println(demoOriginator.getText()); demoOriginator.setText("Esqueça tudo isso! Não quero mais brincar."); demo.saveState(); // CheckPoint. System.out.println(demoOriginator.getText()); demo.undo(); // Volta um estado. System.out.println(demoOriginator.getText()); demo.undo(); // Volta outro estado. System.out.println(demoOriginator.getText()); demo.undo(); // Volta mais um estado. System.out.println(demoOriginator.getText()); } 11/09/2013 @LES/PUC-Rio 12 Um código (JAVA) exemplo Saída do programa: Olá, sou o Memento Olá, sou o Memento. Vamos brincar? Esqueça tudo isso! Não quero mais brincar. Esqueça tudo isso! Não quero mais brincar. Olá, sou o Memento. Vamos brincar? Olá, sou o Memento 11/09/2013 @LES/PUC-Rio 13 Consequências v Vantagens: § Preserva encapsulamento; § Simplifica o uso do Originator; v Desvantagens: § Deve-se atentar para o uso de memória e overhead para armazenar os estados. 11/09/2013 @LES/PUC-Rio 14 Padrões relacionados v Iterator ‒ Memento pode ser usado dentre suas iterações v Command ‒ Podem usar Memento para manter o estado de operações que podem ser desfeitas 11/09/2013 @LES/PUC-Rio 15 Referências bibliográficas • GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; VLISSIDES, John. Design Patterns: Elements of Reusable Object-Oriented Software. • Wikipedia – Memento Pattern http://en.wikipedia.org/wiki/ Memento_pattern [acessado em 08/09/2013] 11/09/2013 @LES/PUC-Rio 16