The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
Essentially, this pattern is made up of three different classes that have three widely accepted names:
-
The Originator: This guy is the guy we are tracking the versions of. He knows what his current state is, how to save his current state and can update his state with either old or new values.
-
The Memento: These guys are the individual versions. Think of these as the save files in a video game. They represent your character's state (progress, gear, quests etc.) at certain points throughout the game.
-
The Caretaker: This guy has a hoarding problem. This is the guy responsible for tracking all of the memento guys. Keeping with the video game analogy, he would represent the file system where all the saves are stored. He knows which ones he has and where they are and can retrieve any at a given time.
To illustrate this design pattern let's pretend that we have created blog and want a way to restore or track previous versions of a post. This means we are going to need three classes:
-
Post
: This class will be the 'originator'. -
PostVersion
: This class will represent a savefile or 'memento'. -
Folder
: This class will be where will store all thePostVersion
's. This class represents the 'caretaker'.
class Post
attr_accessor :title, :body
def initialize(title, body)
@title, @body = title, body
end
def save
PostVersion.new self
end
def rollback(version)
@title = version.title
@body = version.body
end
end
Next we need to create the memento object:
class PostVersion
attr_reader :title, :body
def initialize(post)
@title = post.title
@body = post.body
end
end
Lastly we need to create the caretaker object, in our case the folder system.
class VersionHistory # folder
attr_reader :versions
def initialize
@versions = {}
@version_number = 0
end
def add(version)
@versions[@version_number] = version
@version_number += 1
end
def version(number)
@versions[number]
end
end
Now that we have our classes set up, let's give 'em a whirl:
# Create a Post and a Folder
post = Post.new('My First Post', 'This is my first post. I hope people like it')
folder = VersionHistory.new
# Save the Post to the folder
folder.add(post.save)
# Check Status'
puts post.title
# => My First Post
puts post.body
# => This is my first post. I hope people like it
# Update the Post and save it
post.title = 'My First Post now with a longer title!'
post.body = "I hated my first introduction. I needed to rewrite it - here's the updated version"
folder.add(post.save)
puts post.title
# => My First Post now with a longer title!
puts post.body
# => I hated my first introduction. I needed to rewrite it - here's the updated version
# Everyone hates my updated version, let's restore the old one
post.rollback(folder.version(0))
puts post.title
# => My First Post
puts post.body
# => This is my first post. I hope people like it
In the above example you could have had these three classes as three different database tables allowing users of your application the ability to retrieve past versions of there of their blog posts - a version which currently exists in the Wordpress CMS.
In regards to design here, you may be thinking it would be more approriate to have a Folder attached to a file allowing a more elegant saving (e.g. post.save which would update post.folder) however this breaks encapsulation and scope. The Post class shouldn't know about it's previous lives, all it needs to know is basically it's current state and the fact that it can save its current state and restore a past version.