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?
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.
Java implementation
Lets try to have an overly simplified version of the above example in Java:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!
Kotlin implementation
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:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 restore() function.
Inner classes
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:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Lets say we have a simple application that executes certain actions dictated by an external service.
More particular the external service sends a response that looks like this:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
and there is an executor to help as translate the response to an actual action execution:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is an implementation of the Strategy Pattern where every strategy is a certain action allowing us to add as many actions as we want as long as there is a one-to-one relation with what the response sends:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
One day the external service decides to add a print error action but it does not do it by having a new action value. Instead it decides to have an is error flag which must be taken under consideration when the action value is print!
So the response is now this:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
leading us into having an if in the print action’s code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
which defeats the purpose of the strategy pattern and violates the SRP. Each strategy should do one thing and one thing only. By having an if in the strategy we allow it to implement two actions thus having two reasons to change.
Adding a chain
What we need is a way to allow the representation of a single action value from multiple strategies where each one implements a unique action depending on the values of the response.
This is where the Chain of Responsibility Pattern comes in helping as into having multiple actions tied together, passing the response to each other until one of them can execute it.
There are two ways to implement the pattern depending on the size and state of your project.
Using inheritance
If you have a small project with just a few actions and you don’t mind touching many files you can use inheritance and have each action containing the next in the chain:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If we do not wish to change any of the existing actions then composition is the only way.
What we need is a way to wrap an existing action in an object that will (a) hold the next action in the chain and (b) decide which action will be executed, the wrapped or the next one:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In a recent code review one of my colleagues was concerned that part of the code I wrote might cause us problems because of the null object pattern I chose to use.
The code was something like this:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
and my colleague was referring to the Nobody usage.
That got me thinking. Can this usage of sealed classes be considered as an implementation of the null object pattern? Also, why is the usage of this design pattern a bad thing?
So, what is this pattern? This pattern is one way to solve the
we don’t want a method to return a null value and force ourselves to check it before usage
my definition 😛
Ok, it might be better to have an example:
Lets say that we have a service that returns the workout we did in a particular date. Each workout has the duration of the exercise and if we did not workout on the given date the service returns null (we’ll pretend for a moment that Kotlin does not have null safety or those great extensions like map, sum etc):
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
By introducing a null object, for those days that we did not workout, we can remove the null checks and have a more readable code:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From the example we could easily conclude that this pattern is quite helpful. Right?
Well, as everything in our industry: it depends! When we need a way to have some default values so that our calculations won’t crash and burn then it is a good choice. But if we use it in ways that we hide things we might end up in false successes.
For example, lets assume that we have a storage interface and a factory function that returns the proper storage implementation depending on a given value:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If something is not configured well we might end up using, through out our entire project, the NullStoragethinking that we have saved everything when in reality we lost our data!
In case you missed it there is a space in the cloud-type so no matter how many times we ask for the “cloud”-storage we will get the null-storage back. Silly example with a silly mistake but imagine what could go wrong in real projects!
So, back to the code that started all this
First thing is first:
Q: Is this usage of sealed classes an implementation of the null object pattern?
A: No. The Nobody object is just a representation of a valid state for the AssignedTo concept and does not provide any default values or hides any method usage.
Q:Can sealed classes be used for implementing the null object pattern?
A: Yes. Sealed classes can help us in having many values for one concept but the fact that we can use full fledged classes, that can also inherit a bunch of things, is quite powerful and can be easily misused. I don’t see anything stopping us from implementing the storage-example using sealed classes so we just need to think things carefully.
And now the twist:
As is, the code does not implement the design pattern but with a small change we can make it not only to implement it but to do it badly too! We’ll just move the name property to the parent and have Nobody provide an empty value:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Q:Why is this bad? It does not hide anything, it provides a default value and to be honest we just need to pass “nobody”, instead of an empty string, to the super constructor.
A: Well, this time it depends on where we use the pattern and not how. If this code is part of our core layer then we are allowing this layer to decide on presentation issues! It should be the presentation layer that will check what value the assignedTo has and print “nobody”!
Our previous implementation of AssignedTo was forcing us to make the distinction between all of its values so we did not have much of a choice but to let the print function decide.
In conclusion
Think twice before using the null object pattern and, if you are in a team, try to put code reviews as part of your work flow. It is always good to have a fresh pair of eyes look at your code and even better to have a few people to discuss (and argue) about your choices.
Trying to explain something will make you understand it better!