Hello back again on my little exploration of the UserInterface
implementation of Jeremy Millers StoryTeller. I’d like to start today
with a little excuse (oh my). Although I said last time that the current
post would be focused on the ScreenConductor, I decided to delay that at
least for one post in the series in order to add some content related to
what Jeremy calls ScreenSubject and ScreenFactory. I consider them to be
fairly simple but yet really powerful pattern which can help you a lot
to structure your UI layer, especially the fine line of the UI layer
where the UI infrastructure meets the common application code. It’s also
a very good example of applying the Open Closed Principle.
The common use case for the ScreenSubject
Lets first start with a little description of the usage of the two
patterns in StoryTeller. When application code wants to open a new
screen in StoryTeller it’ll probably use one of the OpenScreen overloads
on the IScreenConductor facade in order to do that.
1 2 3 4 5 | |
The common use case for the ScreenSubject can be easily depicted using a tool we all know quite
well, Visual Studio. Think about it: What happens when you click onto a
source code file in the Source Code Explorer for the first time? Well, a
new tab is created displaying the content of the file. However, if you
click the item in the Source Control Explorer the second time, the
previously created tab gets activated and NO NEW TAB IS CREATED.
How is this implemented
Let’s take a look at how this behavior is implemented in StoryTeller.
The following code snippet shows a very small part of the
ScreenConductor. I’ve inline some comments in order to make it more
visible what is going on there.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
The IScreenSubject abstraction plays a quite important role in this use case. The interface defines
only two methods matching the responsibilities of the ScreenSubject
1 2 3 4 5 | |
As we saw in the ScreenConductor snippet the Matches
method is used as a predicate in order to find the related screen. The
CreateScreen method isn’t directly shown in the snippet, but it’s not
that complicated. It’s responsible for coordinating the creation of a
screen. When I say coordination I’m referring to the fact that the
actual screen creation is the responsibility of the IScreenFactory
(which is just a simple wrapper around StructureMap).
1 2 3 4 5 | |
With this design in place we have a very nice extension point in place to do all kinds of
things around the screen creation. Want to do some deferred data loading
before the screen is shown? That’s your place. Maybe you want to show
some progress indicator while doing this. Guess what, that’s your place
to do this. And the best of it: The screen doesn’t have to know anything
of this, you can free him of this kind of logic. There’re some base
classes which provide some base implementation for different use cases
in StoryTeller. I’m only going to show one of them, the
ScreenSubject<T>. This class adds a bit generics on top of the
ScreenSubject (in order to allow auto-registration using StructureMaps
convention over configuration features).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | |
The basic idea used in this implementation of IScreenSubject is that a ScreenSubject is related
to a single data instance. Coming back to the Visual Studio example,
this could have been represented as something like
ScreenSubject<SourceCodeFile>. Really interesting …
The last code snippet for today I’d like to show is how the ScreenFactory is actually
implemented. As usual really short, not very much code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
I really like the way how StructureMap allows injecting transient parameters in the resolution process …
Closing thoughts
I’m honest with you. I consider ScreenSubject to be one of the most
valuable patterns I’ve learned so far while playing with the StoryTeller
sources. It provides such a nice extension point for screen
initialization. I think it’s one of those patterns you don’t know how
much you miss it until you see it for the first time and recognize how
much it could have helped you in the past. At least that was my
reaction. In the past I’ve put a lot of the functionality which resides
in StoryTeller in ScreenSubject implementations on my screens which
polluted my screens with a lot of initialization code and sometimes
(especially when you have high network latency) doesn’t even really look
good (I bet you know what I’m talking about, don’t you? I bet a lot of
you have seen the frozen screen, too). The ScreenSubject pattern was
introduced to my project last week and what can I say: I’m pretty happy
to have it in our application ;-)