Posted over 5 years ago. Visible to the public.

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'.
Copy
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:

Copy
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.

Copy
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:

Copy
# 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.

Owner of this card:

Avatar
Alexander M
Last edit:
over 5 years ago
by Alexander M
Tags:
Software-Architecture
Posted by Alexander M to Ruby and RoR knowledge base
This website uses short-lived cookies to improve usability.
Accept or learn more