The memento pattern requires the implementation of three distinct entities:
- Memento: a representation of the internal state of Originator, which should be immutable
- Originator: the original object that can produce and consume the Memento, in order to save and restore its own state
- CareTaker: an external object that stores and restores a Memento to an Originator
Let's start by implementing a generic memento pattern. Thanks to Swift and its powerful protocols, we can leverage associated types in order to implement the memento pattern.
Let's start with the first important type, Originator:
protocol Originator {
associatedtype MementoType
func createMemento() -> MementoType
mutating func setMemento(_ memento: MementoType)
}
Originator has just two responsibilities, to create mementos for CareTaker and restore its state using the setMemento method. Note that the state of Originator should be fully restorable through the Memento.
Now, let's have a look at CareTaker:
protocol CareTaker {
associatedtype OriginatorType: Originator
var originator: OriginatorType { get set }
var mementos: [OriginatorType.MementoType] { get set }
mutating func save()
mutating func restore()
}
CareTaker has a reference to the Originator as it should be able to get and restore Memento objects from it.
We'll use the associated type again to store the Memento objects into an array. This will let us push and pop states easily. With the help of extensions, it is even possible to implement the save() and restore() methods:
extension CareTaker {
mutating func save() {
mementos.append(originator.createMemento())
}
mutating func restore() {
guard let memento = mementos.popLast() else { return }
originator.setMemento(memento)
}
}
With those two protocols in place, we can get started with a more complex example that involves saving the state of a large object.