memoryMemento
Memento is a behavioral design pattern that is designed to manage the save and restore previously saved states.
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.
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.