Let’s go right to the ending. When I need a class to construct a Model-View-ViewModel triad, I’ll call that class a “Screen Factory”; the terms I’ve been toying with recently, “Coordinator” and “Director” should be reserved for cases involving triad coordination. How did I get here?
Jeremy Miller called me last week after reading my post on the “Coordinator”. He’s been watching a lot of the MVVM noise we’ve all been making recently as he girds his loins to write a book about “Presentation Patterns”. He recently announced his plans for this book and I encourage you to look at his proposed Table of Contents and send him feedback.
[Warning: I am about to render my recollection of our conversation and to try to represent Jeremy’s thinking accurately. He is more than capable of doing that for himself. But I’m forging ahead because he’s off to Norway, won’t get to these topics for awhile, and I’d rather misremember and flub the concepts than forget altogether.]
Anyway, he read my post and called to talk about it … as well as some of his ideas for the book. He mentioned almost casually that I had misunderstood something he wrote awhile back. He was right about that … as we’ll see.
The gist of my post was that the creation of Model-View-ViewModel (MVVM) triads is a separate responsibility from the internal synchronization among the triad members parts. The VM is dedicated to synchronization of V and M. It should not also assemble either its own triad or other MVVM triads. The job of creating a complex view belongs to something I called a “Coordinator”.
To support my argument about separating creation and synchronization concerns, I cited Jeremy’s “Main Players” article from his 2006 “Build Your Own Cab” series.There he identified something he called the “Screen Conductor” which he said was responsible for “the activation and deactivation lifecycle of the screens within the application.” I glommed on to this, thinking it to be another name for what I’m calling “Coordinator”.
Turns out that isn’t what he had in mind at all. His Screen Conductor doesn’t create screens or views. It oversees a collection of “screens” and decides when to activate (show) or deactivate (hide/close) them. Activation is not creation … although it may require screen creation; Deactivation is not screen destruction … although we may dispose of the screen once it has been deactivated.
Think of Visual Studio. Click an item in the Solution Explorer tree and VS displays the selected solution element in one of its tabs. Click another element and you get another tab. Click the first element again … and instead of a new tab, VS activates the already displayed tab. Try to close that tab and, if you’ve made any changes, you’ll be asked if you want to save or cancel; cancel and the tab remains in place. You’re seeing the Screen Conductor at work, managing a collection of “editors” displayed in tabs.
Note: I don’t know how VS is actually put together. This is an exercise of my imagination; join me, won’t you?
How does the Screen Conductor add a new tab? I figure it finds the association between the item you clicked and an editor for an item of that type. It then might ask something to construct an appropriate editor instance for the selected item … and then adds a tab populated with this editor.
Whatever the actual implementation, we can be sure that the Screen Conductor itself does not construct views. It may know which view to create: a code editor for a C# file, an XML editor for an app.config file. But it doesn’t know squat about how the editors themselves are built. It delegates creational responsibility to something else.
My “Coordinator”, on the other hand, does construct views.
Observe also that there need only be one Screen Conductor in the entire VS application. Screen Conductor is a part of the VS Application Controller … of which there should be exactly one. VS only needs one apparatus to manage the coming and going of tabs.
But there are many “Coordinators” in an application, each capable of constructing a composite “view” of a particular type.
Clearly “Screen Conductor” is not “Coordinator”. This would have been clear to me if I had reread his article with a modicum of care … as I invite you to do.
“So Jeremy”, I said, “if the Screen Conductor doesn’t create the view, what does?”
After a brief digression into open-generic type resolution with StructureMap and whether he actually needed anything specific to create the view, we arrived at the essence of it.
“If you need something to create screens, it would be a Screen Factory.”
“Factory” versus “Coordinator”
When the Screen Conductor determines that the Visual Studio item you clicked - the item that identifies the “Screen Subject” that you’re interested in – is not represented in a tab on screen, it finds the right factory for this kind of subject … and calls it. If you can rely on the IoC container to assemble the pieces; fine … it is acting as your factory. But if you’re not comfortable with IoC handling that task, you’ll want a screen factory class.
And, trust me, you will find that you cannot always rely on IoC / Dependency Injection to create the “view” (the MVVM triad) for you, especially if the “view” is complex and composed of constituent views. You may need a dedicated factory class to assemble such a view. Of course you’ll use IoC to acquire or inject this factory where you need to call it.
My “Coordinator” class is functioning as such a factory. If all it did was create triads, I should have given it a different name … a name with the word “factory” in it.
How did I miss that? The title of my post was “Birth … of M-V-VM Triads”. Hello?
I missed it because I was thinking about an (unnamed) class in one of my applications. That class has additional responsibilities. I was thinking about how it closes and disposes of views. And I was thinking a lot about how it coordinates a set of closely collaborating MVVM triads.
Somehow, in my entire post, I barely mentioned view coordination … which was the reason I called this thing “Coordinator” in the first place. I redress that omission below in a sidebar on “Coordinated Collaborating Views” where I also consider whether you’re “permitted” to write a class that creates, coordinates, and destroys views or if you “must” re-factor such a class because it violates the Single Responsibility Principle (SRP).
But I got ahead of myself. I should have stuck to the challenges of view creation … and called it a factory.
“Screen Factory” or “View Factory”?
What do we call it? Jeremy calls it a “Screen Factory” in keeping with “Screen Conductor”, “Screen Collection”, “Screen Subject”.
We both feel uncomfortable with the word “screen”. “Screen” can mean “everything that appears within the windowed walls of your application.” That’s too big a canvas. In the Visual Studio example, when we say the Conductor manages a collection of screens we are referring to the individual tabs, each one of which holds a “screen.” Is that confusing?
John Papa and I considered calling it a “View Factory” instead. Unfortunately, “View” is at least as overloaded as “Screen.” “View” has the “big” versus “small” problem in reverse. A toolbar or a chart might be implemented as a view within a larger view composition. Some views can be adequately represented with a data template. I wouldn’t want to write a factory for these. Too far down in the weeds. I only need a factory for a view with heft … such as the editor view in the Visual Studio tab.
Moreover, there is potential confusion surrounding the difference between the view type, the “V” in the “M-V-VM” triad, and the view concept which the triad implements. Writing “the MVVM triad” gets old fast. I prefer to just say “View” meaning the conceptual view … and trust that you understand that I mean “that which appears in the UI”, regardless of its implementation. It should be obvious when I’m talking specifically about the V in MVVM (or MVP, or MVC).
But in code, if you see a class named “CustomerOrdersViewFactory” you might wonder if it creates the whole view or just the view instance within the triad. In fact, I may need a view instance factory that determines dynamically which of several alternative view implementations is most appropriate for this particular user at a particular moment. Localization, accessibility requirements, user authorization, … these kinds of factors can lead you to offer several flavors of what is otherwise the same view; the V changes while the M and VM do not.
I’m going to stick with Jeremy’s “Screen Factory” … for now. By “screen” I’ll mean the chunk of visual real estate devoted to a significant user scenario.
Does this work for you?
SideBar: Coordinated Collaborating Views
In the general case, MVVM triad interactions are loosely coupled … often through some pub/sub scheme, e.g., Event Aggregator (EA). There is no need for anything to coordinate them. They just listen and respond. Or they publish an event that another view might care about … and then they immediately forget about it. Remember: EA is always fire-and-forget.
However, I often have a set of “views” that collaborate too closely and too dynamically for a reasonable EA implementation. For example, imagine a “Customer Screen” displayed as a Customer overview positioned above a Details Tab Control. Each detail tab displays particulars of the customer: orders, contacts, credit info, etc.
I would construct this “Customer Screen” compositionally; there would be some MasterDetails mini-shell … the visual framework for the “Customer Screen” … into which I poured the customer overview and each of the detail tab views. Something has to put all of these pieces together (the creational responsibility) and something has to mediate among the composite views to keep them in sync.
Why sync? It is inevitable that something will happen in one of the customer sub-views that affects its colleagues. You could use EA to signal across views. But that gets out of hand pretty quickly. The number of cross-view synchronization events begins to multiply. You start to worry about the diversity of event payloads. The moment you enable viewing of multiple Customer Screens (e.g,, to see “Acme Grocery” and “Fine Food Mart” simultaneously) you have to scope the events by customer so that the two customer screens don’t respond to each other’s messages. You’re reliance upon pub/sub complicates testing and debugging. It’s a mess.
My preferred approach is to provide a common context object to each of the constituent views (I mean their VMs of course) . The context constrains the scope to a single customer and contains all the necessary coordinating machinery. The constituent views remain decoupled from each other … they never address each other directly … but we can still coordinate them with something that is both local and apposite to the task.
What do we call this context? A “coordinator”? A “director”? Either name will do. What I’ve described fits the Mediator pattern as described in GoF where the example class is called “DialogDirector”; it also appears in Wirfs-Brock’s, “Object Design”, as a Coordinator implemented with the Mediator pattern (p. 339).
Look closely at the GoF DialogDirector sample code. Notice that the DialogDirector both creates the constituent components (“widgets”) and coordinates them. It passes itself into the constructor of each of the components it creates.
Argue if you will that the GoF “Director” violates the Single Responsibility Principle (SRP); at least I am in good company.
Is this a problem? Certainly there are at least two dimensions of change. We could change the way we construct the dialog and we could change the way we coordinate its parts. We could refactor DialogDirector into a DialogFactory and a DialogCoordinator. The factory would “make” the widgets, “make” a coordinator, and wire them all together. I have taken this approach with modest success. But I must admit that many found it a tad confusing and overwrought.
I’m not sure it is necessary. Multiple responsibility classes are “bad” because the forces of change impinge upon the responsibilities from different sources at different rates. But the dimensions of change in our coordinator are rarely independent. Am I really going to change how the dialog is constructed without simultaneously changing how its parts are coordinated? Can I change the coordination without knowing about the parts and their instantiation?
Maybe. But let’s be pragmatic, not pedantic. We can always refactor after we take the first bullet.