Implementing the memento pattern

Let's imagine you are building an app that manages a shopping list. On the fly, users can add new items, edit items, and also mark them as done (picked up in-store).

Each element of the shopping list can be represented as an Item object:

struct Item {
var name: String
var done: Bool = false
}

Again, we'll use a struct, as for the memento pattern we want all the features of value types such as immutability and copy-on-write.

Then we need to represent the OriginatorMemento, and CareTaker objects.

A shopping list is just a list. For the sake of our example, we can implement it in many ways, wrapping the list into a ShoppingList as the OriginatorType, using [Item] as the MementoType, or introducing a third object as the CareTaker for managing undos and redos. As we're in Swift, we can also leverage extensions in order to make the whole program more flexible.

The base type of our shopping list, the Array type, is a good candidate for being an Originator. As Array is a value type, we can guarantee that we can store a copy of it. It is also possible to replace all the contents through mutating func, as follows:

extension Array: Originator {
func createMemento() -> [Element] {
return self
}

mutating func setMemento(_ memento: [Element]) {
self = memento
}
}

Now all Array types are Originator objects, and the MementoType is [Element].

The shopping list now can be implemented, but because we don't need another element type, we can call our CareTaker the ShoppingList:

class ShoppingList: CareTaker {

var list = [Item]()

The Memento and Originator objects are part of the CareTaker protocol. Memento will store the different states of the list when calling save() or restore(); the Originator is the list of items itself, as explained:

    var mementos: [[Item]] = []
var originator: [Item] {
get { return list }
set { list = newValue }
}

Let's also add two convenient methods to add items to the list and toggle them:

    func add(_ name: String) {
list.append(Item(name: name, done: false))
}

func toggle(itemAt index: Int) {
list[index].done.toggle()
}
}

Let's add a few extras for printing our shopping list nicely:

  1. First, an extension on String to strike through the characters, as follows:
extension String {
var strikeThrough: String {
return self.reduce("") { (res, char) -> String in
return res + "\(char)" + "\u{0336}"
}
}
}
  1.  Next, a nice description for the shopping list items:
extension Item: CustomStringConvertible {
var description: String {
return done ? name.strikeThrough : name
}
}
  1. Next, let's enhance the description of the shopping list:
extension ShoppingList: CustomStringConvertible {
var description: String {
return list.map {
$0.description
}.joined(separator: "\n")
}
}

Finally, let's consider the following program:

// Create a shopping list
var shoppingList = ShoppingList()
// Add some fish
shoppingList.add("Fish")
// Save to the memento
shoppingList.save()
// Add the Karrots with a typo
shoppingList.add("Karrots")

// Restore to the previous state
shoppingList.restore()

// Check the contents
print("1--\n\(shoppingList)\n\n")

// Add the proper carrots
shoppingList.add("Carrots")
print("2--\n\(shoppingList)\n\n")
shoppingList.save()

// Mark them picked up
shoppingList.toggle(itemAt: 1)
print("3--\n\(shoppingList)\n\n")

// And Restore (didn't pick the right ones)
shoppingList.restore()
print("4--\n\(shoppingList)\n\n")

This will output as follows:

1--
Fish

2--
Fish
Carrots

3--
Fish
C̶a̶r̶r̶o̶t̶s̶

4--
Fish
Carrots

As a possible improvement, we could periodically save the contents of the list. Each time there is a mutation, an addition, or an item is toggled, the ShoppingList could call save.

We'd still be following the memento pattern, as the responsibility of the CareTaker to know why and how to save the Originator object's state would not be violated.