memoryMemento

Description

Memento is a behavioral design pattern that is designed to manage the save and restore previously saved states.

Real World Example

If you are here and reading about design patterns, you surely use some version control system in your daily work with the code, and if you use one, it is certainly GIT. So we will use GIT to discuss today’s problem.

Coding Example

When applying the Memento pattern, we need to create 3 classes for this:
  • Memento-snapshot that stores a given state, sometimes it can also store information such as date, unique identifier or message
  • Originator-responsible for saving and reading the state
  • Caretaker-responsible for keeping all snapshots, adding new ones and reading existing ones
We can compare a memento to a commit in GIT because like memento it can store a given state and additional information like a unique ID. So let’s create a Commit class that can hold a certain state and each time it is created a new randomly generated ID will be created.

# Memento
class Commit
  attr_reader :state, :id
  
  def initialize(state)
    @state = state
    @id = generate_id
  end

  # It generates 4-digit random ID
  def generate_id
    Random.new_seed.digits[0...4].join.to_i
  end
end
The next class we want to create will be Originator, in this example it will be the Editor that is responsible for keeping the current state of the project.

# Originator
class Editor
  attr_accessor :state

  def initialize
    @state = ''
  end
end
Our Caretaker will be the repository, which will allow us to store all commits, add them and read them.

# Caretaker
class Repository
  def initialize
    @commits = []
  end

  # Show all commits
  def log
    @commits
  end

  # Add new commit
  def add(commit)
    @commits << commit
  end

  # Find commit by ID
  def commit(id)
    @commits.find { |c| c.id == id }
  end
end
To use our editor more efficiently, we will add a function to it that will allow us to:
  • initialize repository
  • save current state
  • load specific state

# Originator
class Editor
  attr_accessor :state, :repo

  def initialize
    @state = ''
  end

  # Create new repository
  def init_repo
    @repo = Repository.new
  end

  # Create commit and add to repository
  def commit
    new_commit = Commit.new(state)

    repo.add(new_commit)
  end

  # Restore state from specific commit
  def checkout(id)
    @state = repo.commit(id)&.state
  end
end
We have already created all the classes we need with their functionalities so we can finally use our Memento.

# Create new Editor
editor = Editor.new

# Initialize repository
editor.init_repo

# Set new editor state
editor.state = "Hello World!"

# Create commit of current state, it's ID and save it in the Repo
editor.commit

# Create and commit new state
editor.state = "Hello Mars!"
editor.commit

# Now we can log into a repo history
editor.repo.log # =>
# [#<Commit:0x00007f49a8c785a0 @id=3226, @state="Hello World!">,
#  #<Commit:0x00007f49a545ab00 @id=1703, @state="Hello Mars!">]

# When we know the id of the commit we want to restore we can use it
editor.checkout(3226)
editor.state # => Hello World!
The Memento pattern constructed in this way allows us to store snapshots, but it is also open to modifications. We can easily add new functionalities, e.g. deleting commits or adding a description to commits.