I started playing with the memento pattern for a use case I was researching when I realized that the Kotlin implementation had a, potentially, show stopper in comparison with the Java one:
I could not use a private property from within the same file
Why was that a show stopper? We’ll see, but first, what is the memento pattern?
This pattern is a good way to implement a functionality that helps in restoring previous states. One good example is the undo in our text editors. You can write, edit, delete and then, by hitting undo, take each action back.
There are three main ingredients for this pattern:
- the originator that holds the current state and creates snapshots of itself,
- the memento that, in essence, is the snapshot with perhaps some additional metadata and
- the caretaker that orchestrates the backup/restore of the state
So in our example the originator is the editor which knows what the text is, the carets position etc, the memento a copy of those values and the caretaker can be the interface between the user and the editor.
Lets try to have an overly simplified version of the above example in Java:
Here the editor, besides manipulating text, is able to produce snapshots of its state in a way that only itself can access the state’s values. The
Memento class might be public, in order to allow the caretaker to handle instances of it, but its fields are private and only the originator can read them.
A great way to copy something while having the smallest possible API surface and maximum privacy.
As a matter of fact, here is the caretaker and its usage:
As you can see the
UI uses the editor to write, edit, delete but before that it saves a backup with the editor’s state in order to restore it every time the user hits undo!
So lets move originator and memento to Kotlin.
Ctrl+Alt+Shift+K and boom.. we have a problem:
Kotlin, in contrast with Java, does not allow accessing private properties when in the same file.
What do we do? Well we can always make the properties public:
but this way we, indirectly, expose the editors state:
Another way to implement the pattern is to have
Memento as an interface with no state for the public API and have a private implementation of it for internal usage:
this way we do not expose any state but we do open a bit our API. We now have an interface that can be implemented and given to the
Fortunately Kotlin has inner classes. An inner class can access the outer class’s members but, most importantly, can be extended only from within the outer class. This means that this:
checks all our boxes. We keep the originator’s state private and our overall API small!