Memento (Memo mode)

Memento is a behavioral pattern that is designed to capture and restore the internal state of an object.

Intent: To capture the internal state of an object and save the state outside of the object without breaking encapsulation. You can then restore the object to its original saved state.

In fact, the idea of Memoto mode is very simple, its core is to define a Memoto encapsulation object, which processes the state capture and restore of the original object. Other places do not need to perceive its internal data structure and implementation principle, and the structure of Memoto object itself is very simple. There are only two methods, getState and setState, which are described in more detail later.

For example,

If you don’t understand the above description, it doesn’t matter. Design patterns need to be used in daily work. Examples will help you understand better.

Cancel the redo

If undo redo involves a large number of complex objects, each of which has a different internal state storage structure, it is easy to write redundant code case by case if handled one by one, and when expanding a new object structure (such as embedding PPT), the corresponding structure needs to be processed during undo redo. Memo thinking is equivalent to a unified encapsulation thinking. No matter how the object structure is, it can be stored in a Memoto object. The object state can be set by setState and get by getState, so that for any type of object, the canvas can be accessed through a unified API operation.

Save the game

Those of you who have played games know that many games support setting and loading multiple saves. If we switch to code mode, we might want to have an API for multiple save management:

// Create a game.
const game = new Game()
// Play for a while.
game.play()
// Set an archive.
const gameArchive1 = game.createArchive()
// Play a little longer.
game.play()
// Set an archive.
const gameArchive2 = game.createArchive()
// Play a little longer.
game.play()
// When the character hangs up, the prompt "Please read save" is displayed, and the player selects Save 1.
game.loadArchive(gameArchive1)
// Now the game is back to save 1 and can play again happily.
Copy the code

In fact, in the case of game saving, the saving is a Memoto. When the main process manages the game state, it simply calls createArchive to create the saving, and load to read the saving, so as to realize the complex game saving and reading functions. The whole process does not need to care about the number of internal game states. And how so many states need to be restored one by one, thanks to the design of the memo pattern.

Article draft save

A rich text editor saves drafts of a document in the same way. A simple Memoto object can be used, while a more complex multi-version state management can be achieved by storing multiple Memoto archives similar to the game save mechanism.

Intention to explain

Looking at this, you can see that the memo pattern is very similar to the save and restore of front-end state management. Using Redux’s analogy:

SetState is like the final state processed by the reducer. For the global redux state, it does not care about the business logic (how many reducers there are and what each reducer does). It only needs to know that any reducer is a state object after final processing, and it can be generated and saved.

The same is true for restore. InitState is like getState, and you can completely restore the state at that point in time, regardless of what’s inside the state, simply by pouring in the last generated state.

So the memo pattern has been widely used for a long time. If you look at it carefully, you will find that there is no need to go into too much detail, and how the original design pattern was defined, because after decades of evolution, these design pattern ideas have been integrated into every aspect of the programming framework.

But as usual, let’s paraphrase the intent:

Intent: To capture the internal state of an object and save the state outside of the object without breaking encapsulation. You can then restore the object to its original saved state.

Is focused on the words “don’t destroy the encapsulation”, the maintainability of the program is always the focus of design patterns, whether it be a game file example, or story, for example, the upper frame, all don’t need to know the specific details of the state of the object, and to achieve this is Memoto memos this abstract class.

chart

  • Originator: Indicates the initiator of creating and reading a memo.
  • Memento: a memo that stores the original object state and prevents objects other than Originator from reading it.
  • Caretaker: memo manager, who manages a stack of memos using an array or linked list for undo redo or version management.

The code example

The following example is written in typescript.

Here are the definitions of the three musketeers in memo mode:

/ / memo
class Memento {
  public state: any

  constructor(state: any) {
    this.state = state
  }

  public getState() {
    return this.state
  }
}

// The memo manager
class Caretaker {
  private stack: Memento[] = []

  public getMemento(){
    return this.stack.pop()
  }

  public addMemento(memoto: Memento){
    this.stack.push(memoto)
  }
}

/ / the initiator
class Originator {
  private state: any

  public getState() {
    return this.state
  }

  public setState(state: any) {
    this.state = state
  }

  public createMemoto() {
    return new Memoto(this.state)
  }

  public setMemoto(memoto: Memoto) {
    this.state = memoto.getState()
  }

  public void setMemento(Memento memento){ state = memento.getState(); }}Copy the code

Here is an example of a simplified version of the client:

// Instantiate initiators such as canvas, article manager, game manager
const originator = new Originator()

// Instantiate the memo manager
const caretaker = new Caretaker()

// Set state, corresponding to:
// Canvas component operations.
// Input to the article.
// 游戏的 .play()
originator.setState('hello world')

// The memo manager records the status once, corresponding to:
// Save the canvas.
// Save the article.
// Save the game.
caretaker.setMemento(originator.createMento())

// Restore the state from the memo manager, corresponding to:
// Restore the canvas.
// Read the article.
// The game reads the save.
originator.setMemento(caretaker.getMemento())
Copy the code

In the example above, the memo manager stores the state as an array, so it can implement undo redo. If you want to implement arbitrary reads, you can change the memo structure to Map and read by key. If this is not required, a single Memoto will suffice.

disadvantages

The memo mode stores full state rather than Diff, so it can consume a lot of memory at run time (although in Immutable mode this problem can be greatly mitigated by sharing references).

On the other hand, the memo pattern has been largely integrated into modern frameworks. You already use the memo pattern when using state management tools, so in many cases, you don’t need to mechanically follow the code examples above. The focus of the design pattern is to use it to optimize the maintainability of the application without requiring it to be used exactly as described.

conclusion

The memo mode encapsulates the internal state of the object through the memo object and simplifies the program complexity, which conforms to the principle of “high cohesion, low coupling” always followed by the design mode.

The best example of this is Redux. When all the state of the project is managed using Redux, you will find that it is very easy to undo redo and save read. At this point, do not question why the memo model is still solving the “unexpected problem”. Because Redux itself includes the idea of the memo design pattern.

The discussion address is: Close reading design Patterns – Memento Memento Patterns · Issue #301 · DT-fe /weekly

If you’d like to participate in the discussion, pleaseClick here to, with a new theme every week, released on weekends or Mondays. Front end Intensive Reading – Helps you filter the right content.

Pay attention to the front end of intensive reading wechat public account

Copyright Notice: Freely reproduced – Non-commercial – Non-derivative – Remain signed (Creative Commons 3.0 License)