The memento pattern is a state management technique that you can use to restore your work to a previous state when needed. A common example is the Undo function of a word processor application. After making 10 changes, we can always undo the prior operations and return to the original state before those 10 changes were made. Similarly, an application may remember the most recently opened files and provide a menu of choices so that the user can quickly reopen a previously opened file.
Implementing the memento pattern in Julia is quite simple. We can just store previous states in an array and when making a change, we can push the new state to the array. When we want to undo our actions, we restore the previous state by popping from the array. To illustrate this idea, let's consider the case of a blog post-editing application. We can define the data types as follows:
struct Post
title::String
content::String
end
struct Blog
author::String
posts::Vector{Post}
date_created::DateTime
end
As you can see, a Blog object contains an array of Post objects. By convention, the last element in the array is the current version of the blog post. If there were five posts in the array, then it means that four changes have been made so far. Creating a new blog is as easy, as shown in the following code:
function Blog(author::String, post::Post)
return Blog(author, [post], now())
end
By default, a new blog object contains just one version. As the user makes changes, the array will grow. For convenience, we can provide a version_count function that returns the number of revisions that the user has made so far.
version_count(blog::Blog) = length(blog.posts)
To obtain the current post, we can simply take the last element of the array:
current_post(blog::Blog) = blog.posts[end]
Now, when we have to update the blog, we must push the new version to the array. Here is the function that we use to update the blog with a new title or content:
function update!(blog::Blog;
title = nothing,
content = nothing)
post = current_post(blog)
new_post = Post(
something(title, post.title),
something(content, post.content)
)
push!(blog.posts, new_post)
return new_post
end
The update! function takes a Blog object, and optionally it can take either an updated title, content, or both. Basically, it creates a new Post object and pushes it into the posts array. Undoing is done as follows:
function undo!(blog::Blog)
if version_count(blog) > 1
pop!(blog.posts)
return current_post(blog)
else
error("Cannot undo... no more previous history.")
end
end
We can test it with the following test function:
function test()
blog = Blog("Tom", Post("Why is Julia so great?", "Blah blah."))
update!(blog, content = "The reasons are...")
println("Number of versions: ", version_count(blog))
println("Current post")
println(current_post(blog))
println("Undo #1")
undo!(blog)
println(current_post(blog))
println("Undo #2") # expect failure
undo!(blog)
println(current_post(blog))
end
The output is shown as follows:
As you can see, it is quite easy to implement the memento pattern. We will cover the observer pattern next.