For our first cut at defining this application object, we will have both a
VocabularySet and a screen representing the current user interaction. Because its a simple and flexible navigation model, we’ll include a stack of screens to allow for easy transitions between screens without losing important state.
VocabularySet property captures the current set of works available within the application. This is the set that we’ll be modifying when editing, and the set that we’ll use when running a spelling drill.
We start by declaring the property itself:
When we create a brand new application model, as a part of application startup, we’ll start with an empty vocabulary set.
This gives us a guarantee that the value of
VocabularySet is never null, making it easier to write some of our later code.
Following the pattern we’ve already used, we’ll provide a pair of methods to allow manipulation of the property.
WithVocabularySet() allows us to completely substitute a new set of words:
ReferenceEquals() allows us to take a memory saving shortcut when nothing actually changes.
I’m still considering whether I approve of having the parameter validation for
vocabularySet occur quite so late in the method.
On the one hand, we get all the protection we need even though the validation doesn’t happen as the very first step in the method. On the other, common practice is to have all these kinds of checks up front before any actual logic, and I’m not entirely comfortable with moving away from this practice. We get away with it here because there are no side effects to the code run before the validation check.
UpdateVocabularySet() method allows us to apply a transformation to the set of words we already have.
Here we have the parameter validation up front, in a more conventional location. Again, we provide a short circuit if the transformation doesn’t actually make any changes to the set.
Both of these transformation methods are based on a private constructor that creates a near clone, a technique we’ve used before.
Having good unit tests to make sure that our application model behaves as expected is critical. We don’t want to be trying to troubleshoot a dozen moving parts in one go when we get to the point of interacting with a running application.
I’m partial to using a three part naming convention (unit/scenario/expectation) as promoted in the book The Art of Unit Testing, though with a twist as suggested by Phil Haack of grouping related tests together.
These test names can be treated as a kind of specification for the behavior of the class. For example, here are the tests I wrote for functionality related to the
ValidationSet property of our application model:
- With VocabularySet
- Given null vocabulary set, throws exception
- Given vocabulary set, updates property
- Update vocabulary set
- Given null, throws exception
- Given transformation
- Receives current vocabulary set
- Updates property to returned value
- When transformation returns existing set, returns existing app
To handle the screens, we’ll add two members to our class - a reference to the current screen, and a stack of prior screens that we can restore when we close the current screen.
Of course, our constructor needs to change:
Once again, we see parameter validation happening relatively late in the method.
It would be easy to follow the existing pattern with our methods that manipulate screens. For example, we could write
This would be a mistake.
While we don’t want to implement anything more than necessary, it’s important that our methods provide some useful semantics to govern how our application works. Users don’t think in terms of pushing or popping screens - they think in terms of opening and closing them.
It’s easy to fall into the trap of defining everything in terms of CRUD operations (create, read, update, delete) when a more expressive set of operation provides increased clarity.
Here we open a new screen, preserving the old one on our stack to recall it later. Closing a window is the obvious inverse.
Note that we’ve included a check to make sure we never close the last screen; this ensures we will always have a visible UX available to the user.
Lastly, we’ll provide a way to modify the current screen - this might be a small change to the existing screen, or a transition to a completely different screen.
As you might have already guessed, we need to update our private constructor to accommodate screen navigation:
As I write this it seems clear that the
WordTutorApplication class has very low coupling. On the one hand, we have the vocabulary set and the methods to manipulate it. On the other, a stack of screens and the methods to manipulate them.
The two do not interact.
This is a bit of a code smell, and indicates that we might need to re-factor this class into separate responsibilities in the future. For now however this seems good enough.