Thursday, December 31, 2015

Memento Design Pattern



https://sourcemaking.com/design_patterns/memento
Intent
  • Without violating encapsulation, capture and externalize an object's internal state so that the object can be returned to this state later.
  • A magic cookie that encapsulates a "check point" capability.
  • Promote undo or rollback to full object status.
Need to restore an object back to its previous state (e.g. "undo" or "rollback" operations).

Motivation
It is sometimes necessary to capture the internal state of an object at some point and have the ability to restore the object to that state later in time. Such a case is useful in case of error or failure. Consider the case of a calculator object with an undo operation such a calculator could simply maintain a list of all previous operation that it has performed and thus would be able to restore a previous calculation it has performed. This would cause the calculator object to become larger, more complex, and heavyweight, as the calculator object would have to provide additional undo functionality and should maintain a list of all previous operations. This functionality can be moved out of the calculator class, so that an external (let's call it undo manager class) can collect the internal state of the calculator and save it. However providing the explicit access to every state variable of the calculator to the restore manager would be impractical and would violate the encapsulation principle.

  • The intent of this pattern is to capture the internal state of an object without violating encapsulation and thus providing a mean for restoring the object into initial state when needed.

Memento Pattern Implementation - UML Class Diagram 
  • Memento
    • Stores internal state of the Originator object. The state can include any number of state variables.
    • The Memento must have two interfaces, an interface to the caretaker. This interface must not allow any operations or any access to internal state stored by the memento and thus honors encapsulation. The other interface is to the originator and allows the originator to access any state variables necessary to for the originator to restore previous state.
  • Originator
    • Creates a memento object capturing the originators internal state.
    • Use the memento object to restore its previous state.
  • Caretaker
    • Responsible for keeping the memento.
    • The memento is opaque to the caretaker, and the caretaker must not operate on it.
A Caretaker would like to perform an operation on the Originator while having the possibility to rollback. The caretaker calls the createMemento() method on the originator asking the originator to pass it a memento object. At this point the originator creates a memento object saving its internal state and passes the memento to the caretaker. The caretaker maintains the memento object and performs the operation. In case of the need to undo the operation, the caretaker calls the setMemento() method on the originator passing the maintained memento object. The originator would accept the memento, using it to restore its previous state.

http://www.journaldev.com/1734/memento-design-pattern-in-java-example-tutorial
Memento design pattern is used when we want to save the state of an object so that we can restore later on. Memento pattern is used to implement this in such a way that the saved state data of the object is not accessible outside of the object, this protects the integrity of saved state data.
Memento pattern is implemented with two objects – Originator and Caretaker. Originator is the object whose state needs to be saved and restored and it uses an inner class to save the state of Object. The inner class is called Memento and its private, so that it can’t be accessed from other objects.
Caretaker is the helper class that is responsible for storing and restoring the Originator’s state through Memento object. Since Memento is private to Originator, Caretaker can’t access it and it’s stored as a Object within the caretaker.
http://www.avajava.com/tutorials/lessons/memento-pattern.html
The memento pattern is a behavioral design pattern. The memento pattern is used to store an object's state so that this state can be restored at a later point. The saved state data in the memento object is not accessible outside of the object to be saved and restored. This protects the integrity of the saved state data.
In this pattern, an Originator class represents the object whose state we would like to save. A Memento class represents an object to store the state of the Originator. The Memento class is typically a private inner class of the Originator. As a result, the Originator has access to the fields of the memento, but outside classes do not have access to these fields. This means that state information can be transferred between the Memento and the Originator within the Originator class, but outside classes do not have access to the state data stored in the Memento.
The memento pattern also utilizes a Caretaker class. This is the object that is responsible for storing and restoring the Originator's state via a Memento object. Since the Memento is a private inner class, the Memento class type is not visible to the Caretaker. As a result, the Memento object needs to be stored as an Object within the Caretaker.
// originator - object whose state we want to save
public class DietInfo {

 String personName;
 int dayNumber;
 int weight;

 public DietInfo(String personName, int dayNumber, int weight) {
  this.personName = personName;
  this.dayNumber = dayNumber;
  this.weight = weight;
 }

 public String toString() {
  return "Name: " + personName + ", day number: " + dayNumber + ", weight: " + weight;
 }

 public void setDayNumberAndWeight(int dayNumber, int weight) {
  this.dayNumber = dayNumber;
  this.weight = weight;
 }

 public Memento save() {
  return new Memento(personName, dayNumber, weight);
 }

 public void restore(Object objMemento) {
  Memento memento = (Memento) objMemento;
  personName = memento.mementoPersonName;
  dayNumber = memento.mementoDayNumber;
  weight = memento.mementoWeight;
 }

 // memento - object that stores the saved state of the originator
 private class Memento {
  String mementoPersonName;
  int mementoDayNumber;
  int mementoWeight;

  public Memento(String personName, int dayNumber, int weight) {
   mementoPersonName = personName;
   mementoDayNumber = dayNumber;
   mementoWeight = weight;
  }
 }
}
// caretaker - saves and restores a DietInfo object's state via a memento
// note that DietInfo.Memento isn't visible to the caretaker so we need to cast the memento to Object
public class DietInfoCaretaker {

 Object objMemento;

 public void saveState(DietInfo dietInfo) {
  objMemento = dietInfo.save();
 }

 public void restoreState(DietInfo dietInfo) {
  dietInfo.restore(objMemento);
 }

}
http://www.tutorialspoint.com/design_pattern/memento_pattern.htm

One of the best real life example is the text editors where we can save it’s data anytime and use undo to restore it to previous saved state. 
public class FileWriterUtil {
    private String fileName;
    private StringBuilder content;
     
    public FileWriterUtil(String file){
        this.fileName=file;
        this.content=new StringBuilder();
    }
     
    @Override
    public String toString(){
        return this.content.toString();
    }
     
    public void write(String str){
        content.append(str);
    }
     
    public Memento save(){
        return new Memento(this.fileName,this.content);
    }
     
    public void undoToLastSave(Object obj){
        Memento memento = (Memento) obj;
        this.fileName= memento.fileName;
        this.content=memento.content;
    }
     
     
    private class Memento{
        private String fileName;
        private StringBuilder content;
         
        public Memento(String file, StringBuilder content){
            this.fileName=file;
            //notice the deep copy so that Memento and FileWriterUtil content variables don't refer to same object
            this.content=new StringBuilder(content);
        }
    }
}
Notice the Memento inner class and implementation of save and undo methods.
public class FileWriterCaretaker {
    private Object obj;
     
    public void save(FileWriterUtil fileWriter){
        this.obj=fileWriter.save();
    }
     
    public void undo(FileWriterUtil fileWriter){
        fileWriter.undoToLastSave(obj);
    }
}
Notice that caretaker object contains the saved state in the form of Object, so it can’t alter its data and also it has no knowledge of it’s structure.

one of the thing needs to take care is that Memento class should be accessible only to the Originator object. Also in client application, we should use caretaker object for saving and restoring the originator state.
Also if Originator object has properties that are not immutable, we should use deep copy or cloning to avoid data integrity issue like I have used in above example. We can use Serialization to achieve memento pattern implementation that is more generic rather than Memento pattern where every object needs to have it’s own Memento class implementation.
One of the drawback is that if Originator object is very huge then Memento object size will also be huge and use a lot of memory.
http://www.javacodegeeks.com/2015/09/memento-design-pattern.html
03public class Originator {
04
05    private double x;
06    private double y;
07
08    private String lastUndoSavepoint;
09    CareTaker careTaker;
10
11    public Originator(double x, double y,CareTaker careTaker){
12        this.x = x;
13        this.y = y;
14
15        this.careTaker = careTaker;
16
17        createSavepoint("INITIAL");
18    }
36    public void createSavepoint(String savepointName){
37        careTaker.saveMemento(new Memento(this.x, this.y), savepointName);
38        lastUndoSavepoint = savepointName;
39    }
40
41    public void undo(){
42        setOriginatorState(lastUndoSavepoint);
43    }
44
45    public void undo(String savepointName){
46        setOriginatorState(savepointName);
47    }
48
49    public void undoAll(){
50        setOriginatorState("INITIAL");
51        careTaker.clearSavepoints();
52    }
53
54    private void setOriginatorState(String savepointName){
55        Memento mem = careTaker.getMemento(savepointName);
56        this.x = mem.getX();
57        this.y = mem.getY();
58    }
65}
03public class Memento {
04
05    private double x;
06    private double y;
07
08    public Memento(double x, double y){
09        this.x = x;
10        this.y = y;
11    }
20}
The Memento class is used to store the state of the Originator and stored by the care taker. The class does not have any setter methods, it is only used to get the state of the object.
06public class CareTaker {
07
08    private final Map<String, Memento>savepointStorage = newHashMap<String, Memento>();
09
10    public void saveMemento(Memento memento,String savepointName){
11        System.out.println("Saving state..."+savepointName);
12        savepointStorage.put(savepointName, memento);
13    }
14
15    public Memento getMemento(String savepointName){
16        System.out.println("Undo at ..."+savepointName);
17        return savepointStorage.get(savepointName);
18    }
19
20    public void clearSavepoints(){
21        System.out.println("Clearing all save points...");
22        savepointStorage.clear();
23    }
25}

4. When to use the Memento Pattern
Use the Memento Pattern in the following cases:
A snapshot of (some portion of) an object’s state must be saved so that it can be restored to that state later, and
A direct interface to obtaining the state would expose implementation details and break the object’s encapsulation.
5. Memento Pattern in JDK
java.util.Date
java.io.Serializable

Some problems with this pattern is that the saving or restoring of state can be a time consuming process. Used incorrectly, it can expose the internal structure of your object, thus allowing any other object to change the state of your object.
'The memento pattern seems to be more flexible. You ask an object for its current state, and it can track whatever changes are made using special semantics, rather than dumping the entire state and rereading later.' -- PeterSchofield

http://www.cnblogs.com/java-my-life/archive/2012/06/06/2534942.html
  备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。
  备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。

备忘录模式的结构

  备忘录模式的结构图如下所示
  备忘录模式所涉及的角色有三个:备忘录(Memento)角色、发起人(Originator)角色、负责人(Caretaker)角色

  备忘录(Memento)角色

  备忘录角色又如下责任:
  (1)将发起人(Originator)对象的内战状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。
  (2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。
  备忘录有两个等效的接口:
  ●  窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。
  ●  宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。

  发起人(Originator)角色

  发起人角色有如下责任:
  (1)创建一个含有当前的内部状态的备忘录对象。
  (2)使用备忘录对象存储其内部状态。

  负责人(Caretaker)角色

  负责人角色有如下责任:
  (1)负责保存备忘录对象。
  (2)不检查备忘录对象的内容。

 “白箱”备忘录模式的实现

  备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。
  “白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。因此白箱实现仍然是有意义的。
public class Originator {

    private String state;
    /**
     * 工厂方法,返回一个新的备忘录对象
     */
    public Memento createMemento(){
        return new Memento(state);
    }
    /**
     * 将发起人恢复到备忘录对象所记载的状态
     */
    public void restoreMemento(Memento memento){
        this.state = memento.getState();
    }
    
    public String getState() {
        return state;
    }
    
    public void setState(String state) {
        this.state = state;
        System.out.println("当前状态:" + this.state);
    }
    
}
public class Caretaker {

    private Memento memento;
    /**
     * 备忘录的取值方法
     */
    public Memento retrieveMemento(){
        return this.memento;
    }
    /**
     * 备忘录的赋值方法
     */
    public void saveMemento(Memento memento){
        this.memento = memento;
    }
}
    public static void main(String[] args) {
        
        Originator o = new Originator();
        Caretaker c = new Caretaker();
        //改变负责人对象的状态
        o.setState("On");
        //创建备忘录对象,并将发起人对象的状态储存起来
        c.saveMemento(o.createMemento());
        //修改发起人的状态
        o.setState("Off");
        //恢复发起人对象的状态
        o.restoreMemento(c.retrieveMemento());
        
        System.out.println(o.getState());
    }

 “黑箱”备忘录模式的实现

  备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。
  在JAVA语言中,实现双重接口的办法就是将备忘录角色类设计成发起人角色类的内部成员类。
  将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面;在外部提供一个标识接口MementoIF给Caretaker以及其他对象。这样,Originator类看到的是Menmento的所有接口,而Caretaker以及其他对象看到的仅仅是标识接口MementoIF所暴露出来的接口。
  使用内部类实现备忘录模式的类图如下所示。
  发起人角色类Originator中定义了一个内部的Memento类。由于此Memento类的全部接口都是私有的,因此只有它自己和发起人类可以调用。
public class Originator {

    private String state;
    
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
        System.out.println("赋值状态:" + state);
    }
    /**
     * 工厂方法,返还一个新的备忘录对象
     */
    public MementoIF createMemento(){
        return new Memento(state);
    }
    /**
     * 发起人恢复到备忘录对象记录的状态
     */
    public void restoreMemento(MementoIF memento){
        this.setState(((Memento)memento).getState());
    }
    
    private class Memento implements MementoIF{
        
        private String state;
        /**
         * 构造方法
         */
        private Memento(String state){
            this.state = state;
        }
        
        private String getState() {
            return state;
        }
        private void setState(String state) {
            this.state = state;
        }
    }
}
接口MementoIF,这是一个标识接口,因此它没有定义出任何的方法。

public interface MementoIF {

}
  负责人角色类Caretaker能够得到的备忘录对象是以MementoIF为接口的,由于这个接口仅仅是一个标识接口,因此负责人角色不可能改变这个备忘录对象的内容。
public class Caretaker {

    private MementoIF memento;
    /**
     * 备忘录取值方法
     */
    public MementoIF retrieveMemento(){
        return memento;
    }
    /**
     * 备忘录赋值方法
     */
    public void saveMemento(MementoIF memento){
        this.memento = memento;
    }
}

多重检查点

  前面所给出的白箱和黑箱的示意性实现都是只存储一个状态的简单实现,也可以叫做只有一个检查点。常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。
  备忘录模式可以将发起人对象的状态存储到备忘录对象里面,备忘录模式可以将发起人对象恢复到备忘录对象所存储的某一个检查点上

 “自述历史”模式

  所谓“自述历史”模式(History-On-Self Pattern)实际上就是备忘录模式的一个变种。在备忘录模式中,发起人(Originator)角色、负责人(Caretaker)角色和备忘录(Memento)角色都是独立的角色。虽然在实现上备忘录类可以成为发起人类的内部成员类,但是备忘录类仍然保持作为一个角色的独立意义。在“自述历史”模式里面,发起人角色自己兼任负责人角色。
 备忘录角色有如下责任:
  (1)将发起人(Originator)对象的内部状态存储起来。
  (2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。
  发起人角色有如下责任:
  (1)创建一个含有它当前的内部状态的备忘录对象。
  (2)使用备忘录对象存储其内部状态。
  客户端角色有负责保存备忘录对象的责任。

  源代码

  窄接口MementoIF,这是一个标识接口,因此它没有定义出任何的方法。
public interface MementoIF {

}
  发起人角色同时还兼任负责人角色,也就是说它自己负责保持自己的备忘录对象。
  由于“自述历史”作为一个备忘录模式的特殊实现形式非常简单易懂,它可能是备忘录模式最为流行的实现形式。


Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts