Memento

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 the PostVersion'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.

Alexander M over 5 years ago
This website uses short-lived cookies to improve usability.
Accept or learn more