Understand the memo mode of 23 design modes

Posted by Klojum on Fri, 17 Dec 2021 07:17:48 +0100

Memo mode

Reference

[1] bugstack.cn/md/develop/...

[2] c.biancheng.net/view/1397.h...

[3] refactoringguru.cn/design-patt...

[4] cmsblogs.com/article/140...

[5] blog.csdn.net/lovelion

What is memo mode?

Memo mode is a behavior design mode, which allows saving and restoring the previous state of an object without exposing the implementation details of the object, that is, capturing the internal state of an object and saving the state outside the object without destroying the encapsulation, so that the object can be restored to the original saved state when necessary in the future.

This mode is also called snapshot mode.

I'm a little confused. How to save and restore the previous state of the object without exposing the details of the object (which can also be understood as rollback or undo function)?

We know that if we want to generate a snapshot or copy of an object, we may need to traverse all member variables of the object and copy and save its current value. However, this can only be used when the object does not have strict access control over its content.

However, most objects will be declared as private private objects to store important data to ensure data security. If we want other objects to be able to save and read the snapshot, we probably need to set the member variable of the snapshot to public.

So here comes the question...?

Or it will expose all the internal details of the class and make it too fragile; Or access to its state will be restricted and the snapshot cannot be generated. So, are there any other ways to implement the "undo" function?

Solution

All the problems we have just encountered are caused by the "Breakage" of the package. Some objects try to work beyond their responsibilities. Because some behaviors need to get data, they invade the private space of other objects instead of letting them do the actual work.

Memorandum structure

Memo mode delegates the creation of state snapshots to the Originator object, the owner of the actual state. In this way, other objects no longer need to copy the editor state from the "outside". The editor class has full access to its state, so it can generate snapshots on its own.

The mode recommends that a copy of the object state be stored in a special object called memo. Except for the object that creates the memo, no object can access the contents of the memo. Other objects must interact with the memo using a restricted interface, and they can obtain the metadata of the snapshot (creation time, operation name, etc.), but the state of the original object in the snapshot cannot be obtained.

The simple explanation is that we only need to let the object itself generate memos by itself (construction, cloning, serialization, etc.), and the generated snapshot is memo memos, which can only be accessed by the Originator of the created object. Then we provide access to memo memos through an interface that limits access permissions

Implementation mode

  • Implementation based on nested classes
  • Implementation based on intermediate interface

Implementation based on nested classes

  1. Originator is a common class. It can create a memo and store its current internal state, or use the memo to restore its internal state. Generally, the class that needs to save the internal state is designed as the originator.
  2. A memo is a value object of the initiator state snapshot (value object). The usual approach is to set the memo to immutable, pass data through the constructor at one time, store the internal state of the initiator, and decide which internal state to save according to the initiator. Generally, the design of the memo can refer to the design of the initiator and determine the attributes in the memo class according to the actual needs. It should be noted that in addition to the original Apart from the responsible human, memo objects cannot be directly used by other classes. The design of the initiator will have different implementation mechanisms in different programming languages
  3. Caretaker, also known as the manager, is responsible for keeping the memo and only knows "when" and "why" Capture the status of the originator and when to restore the status, but you cannot operate or check the contents of the memo. One or more memo objects can be stored in the responsible human. It is only responsible for storing objects, not modifying objects, and does not need to know the implementation details of objects.

The person in charge records the historical state of the initiator by saving the memo stack or collection. When the originator needs to trace back the historical state, the person in charge will get the top memo from the stack and pass it to the originator's restore method (the set is transformed by index subscript)

Access rights

In this implementation, the memo class will be nested in the originator. In this way, the initiator can access the member variables and methods of the memo, even if these methods are declared private. On the other hand, the owner has limited access to the member variables and methods of the memo: they can only save the memo in the stack, not modify its state.

When implemented in Java, the Memento class and the Originator class are generally encapsulated by defining them in the same package. In the Java language, the default access identifier can be used to define the Memento class, that is, to ensure that it is visible in the package. Only the Originator class can access Memento, which restricts the access of other classes to Memento.

scene

Suppose we want to design the repentance function of chess now, how should we design it?

  • Let's analyze. To record the chess position of a player at a historical time, we need to record the coordinates of the pieces and other information. Obviously, the class that integrates this information is the Originator initiator class

  • Then we need to record the originator, that is, generate a memo memo

  • Another person in charge will store the memo and operate it

Structure diagram

//Chess subclass: initiator
class Chessman {
    private String label;
    private int x;
    private int y;
    public Chessman(String label,int x,int y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }
    public void setLabel(String label) {
        this.label = label;
    }
    public void setX(int x) {
        this.x = x;
    }
    public void setY(int y) {
        this.y = y;
    }
    public String getLabel() {
        return (this.label);
    }
    public int getX() {
        return (this.x);
    }
    public int getY() {
        return (this.y);
    }
    //Save status
    public ChessmanMemento save() {
        return new ChessmanMemento(this.label,this.x,this.y);
    }
    //Restore state
    public void restore(ChessmanMemento memento) {
        this.label = memento.getLabel();
        this.x = memento.getX();
        this.y = memento.getY();
    }
}
//Chess pieces memo: Memo
class ChessmanMemento {
    private String label;
    private int x;
    private int y;
    public ChessmanMemento(String label,int x,int y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }
    public void setLabel(String label) {
        this.label = label;
    }
    public void setX(int x) {
        this.x = x;
    }
    public void setY(int y) {
        this.y = y;
    }
    public String getLabel() {
        return (this.label);
    }
    public int getX() {
        return (this.x);
    }
    public int getY() {
        return (this.y);
    }
}
//Chess piece memo Management: person in charge
import java.util.*;
class MementoCaretaker {
    //Define a collection to store multiple memos
    private ArrayList mementolist = new ArrayList();
    public ChessmanMemento getMemento(int i) {
        return (ChessmanMemento)mementolist.get(i);
    }
    public void setMemento(ChessmanMemento memento) {
        mementolist.add(memento);
    }
}
Copy code

client

class Client {
    private static int index = -1; //Define an index to record the location of the current state
    private static MementoCaretaker mc = new MementoCaretaker();
    public static void main(String args[]) {
        Chessman chess = new Chessman("vehicle",1,1);
        play(chess);        
        chess.setY(4);
        play(chess);
        chess.setX(5);
        play(chess);
        undo(chess,index);
        undo(chess,index);
        redo(chess,index);
        redo(chess,index);
    }
    //play chess
    public static void play(Chessman chess) {
        mc.setMemento(chess.save()); //Save memo
        index ++;
        System.out.println("piece" + chess.getLabel() + "The current location is:" + "The first" + chess.getX() + "that 's ok" + "The first" + chess.getY() + "Columns.");
    }
    //Repentance chess
    public static void undo(Chessman chess,int i) {
        System.out.println("******Repentance chess******");
        index --;
        chess.restore(mc.getMemento(i-1)); //Undo to previous memo
        System.out.println("piece" + chess.getLabel() + "The current location is:" + "The first" + chess.getX() + "that 's ok" + "The first" + chess.getY() + "Columns.");
    }
    //Undo repentance
    public static void redo(Chessman chess,int i) {
        System.out.println("******Undo repentance******");
        index ++;
        chess.restore(mc.getMemento(i+1)); //Revert to next memo
        System.out.println("piece" + chess.getLabel() + "The current location is:" + "The first" + chess.getX() + "that 's ok" + "The first" + chess.getY() + "Columns.");
    }
}
Copy code

Compilation results

The current position of the chess cart is: row 1, column 1.
The current position of the chess cart is: row 1, column 4.
The current position of the chess cart is: row 1, column 4.
The current position of the chess cart is row 5 and column 4.
******Repentance chess******
The current position of the chess cart is: row 1, column 4.
Copy code

Summary

Memo: it is a very special object. Only the initiator has control over it, and the person in charge is only responsible for management. Other classes cannot access the memo, so we need to encapsulate the memo

For each role

  • For the initiator, it can call all the information of the memo and allow the initiator to access all the data required to return to the previous state;
  • As for the person in charge, he is only responsible for the preservation of the memo and transmitting the memo to other objects;
  • For other objects, you only need to take out the memo object from the person in charge and restore the state of the initiator object, without paying attention to the details of saving the memo.


Author: HappyChan
Link: https://juejin.cn/post/7041887774508580895
Source: rare earth Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Topics: Java Design Pattern Back-end Programmer