Design Pattern: Memento Pattern in Go

Memento pattern is used to store the sequence/history of actions performed over time on an object. By storing the history of the state change of the object, we can revert the changes and perform an undo of an action.

This article demonstrates Memento pattern implementations in Golang. Check the implementation process and examples.

Implementation

Follow the steps below to implement the Memento pattern in Golang-

  • We need a memento struct, this is the class of which we want to store the history. Make sure there are methods to get the field(states) values. This struct can be pre-existing.
  • Create an originator struct. This struct has states/fields to store the values for the memento. This struct also has methods to generate a new memeto struct and return that.
  • Create a caretaker struct. This sturct is responsible for storing a list of the memento struct.

Examples

Let’s take a look at a few examples.

Example #1: General Memento

This is a general memento pattern implementation, to demonstrate all the elements in Golang.

Memento

  • Create file “memento.go”.
  • Create struct “Memento”.
  • Define field “state” of type string. There can be other fields, as per the requirement.
  • Define method “NewMemento”. This method initiates a new “Memento” struct, sets the state, and returns that.
  • Create method to get the state value- “GetState”.
// memento.go

package main

type Memento struct {
	state string
}

func NewMemento(state string) (memento *Memento) {
	memento = &Memento{}
	memento.state = state
	return
}

func (memento *Memento) GetState() string {
	return memento.state
}

Originator

  • Create file “originator.go”.
  • Create struct “Originator”.
  • Define the field “state” which is of type string. This field is for storing the state of the memento.
  • Define the method “NewOriginator” for initiating a new “Originator” struct.
  • Define method “SetMemento”. This method is used to generate a new memento struct and return that.
  • There are a few other utility methods s per requirement.
// originator.go

package main

import "fmt"

type Originator struct {
	state string
}

func NewOriginator() (originator *Originator) {
	originator = &Originator{}
	return
}

func (originator *Originator) GetMementoState(memento *Memento) {
	originator.state = memento.GetState()
}

func (originator *Originator) GetState() (string) {
	return originator.state
}

func (originator *Originator) SetMemento() (*Memento) {
	fmt.Printf("Memento Saved with timestamp => %s\n", originator.state)
	return NewMemento(originator.state)
}

func (originator *Originator) SetState(state string) {
	originator.state = state
}

Caretaker

  • Create file “caretaker.go”.
  • Create struct “Caretaker”.
  • Define a field named “mementoList” for storing a slice of type “Memento”.
  • Define methods to add/remove/get items from the slice.
// caretaker.go

package main

type Caretaker struct {
	mementoList []Memento
}

func NewCaretaker() (caretaker *Caretaker) {
	caretaker = &Caretaker{}
	return
}

func (caretaker *Caretaker) Add(memento Memento) {
	caretaker.mementoList = append(caretaker.mementoList, memento)
}

func (caretaker *Caretaker) GetByIndex(index int) Memento {
	return caretaker.mementoList[index]
}

func (caretaker *Caretaker) GetCurrent() Memento {
	return caretaker.mementoList[len(caretaker.mementoList)-1]
}

func (caretaker *Caretaker) Undo() {
	caretaker.mementoList = caretaker.mementoList[:len(caretaker.mementoList)-1]
}

Demo

To use the implementation create a new “Caretaker” and “Originator”.

Use the originator to create new memento, and use the caretaker to manage the history of the memento.

// main.go

package main

import (
	"fmt"
	"time"
)

func main() {
	caretaker := NewCaretaker()
	originator := NewOriginator()

	originator.SetState(fmt.Sprintf("Time - 1 : %v", time.Now()))
	caretaker.Add(*originator.SetMemento())

	originator.SetState(fmt.Sprintf("Time - 2 : %v", time.Now()))
	caretaker.Add(*originator.SetMemento())

	originator.SetState(fmt.Sprintf("Time - 3 : %v", time.Now()))
	caretaker.Add(*originator.SetMemento())

	fmt.Println("---------------------------------------------")
	fmt.Println("Check state at index 1 (index starts at 0):")
	stateAtIndex1 := caretaker.GetByIndex(1)
	fmt.Println(stateAtIndex1.GetState())

	fmt.Println("---------------------------------------------")
	
	fmt.Println("Check last state:")
	lastState := caretaker.GetCurrent()
	fmt.Println(lastState.GetState())

	fmt.Println("---------------------------------------------")
	
	fmt.Println("Undoing last state")
	caretaker.Undo()

	fmt.Println("---------------------------------------------")
	
	fmt.Println("Check last state after undo:")
	lastStateAfterUndo := caretaker.GetCurrent()
	fmt.Println(lastStateAfterUndo.GetState())
}

Output

Output of the demo code will be as below-

Memento Saved with timestamp => Time - 1 : 1689909369587
Memento Saved with timestamp => Time - 2 : 1689909369588
Memento Saved with timestamp => Time - 3 : 1689909369589

---------------------------------------------

Check state at index 1 (index starts at 0):
Time - 2 : 1689909369588

---------------------------------------------

Check last state:
Time - 3 : 1689909369589

---------------------------------------------

Undoing last state

---------------------------------------------

Check last state after undo:
Time - 2 : 1689909369588

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check the Memento pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.