다음을 통해 공유


100 Model/View/ViewModels of Mt. Fuji

Trapped deep within the pragmatic hacker that is the essential me, there still lurks a computer scientist.  The computer scientist isn't even screaming to get out; he's been so long buried in the deepest dungeons that he is grown to accept his fate, adopted a pet rat, learned to love gruel and made plans to one day move into a nice dry corner oubliette he can call his own.  Occasionally however, he does mutter something about my cavalier definitions of the terms I use.

Case in point:  Model/View/ViewModel.  My team has been using the pattern feverishly for a couple of years, and we still don't have a clean definition of the term ViewModel.  Model is well understood.   View is well understood, if slightly stricter than the MVC definition.  But what is this ViewModel thing?  Fine...my team uses this word to mean what we want it to mean.  But that makes it much harder to introduce and can lead to all sorts of confusion.

One problem is ViewModel actually combines several separate, orthogonal concepts:  view state, value conversion, commands for performing operations on the model.  As originally taught to us by the two architects who did the original Sparkle prototyping (and were premier Smalltalk hackers from way back), the pattern could actually have been called Model/View/ViewState or Model/View/AbstractView.  The pattern was to take a pure Model, create an abstract view that contained state, and data bind a View created with a visual designer to that abstract view.  That's a nice clean, formal pattern.  Let me lay it out again:

The Model/View/AbstractView pattern

Given a UI agnostic Model, create an Abstract View that contains state such as selection and modes,  and then use a tool to create a View that is data bound to that abstraction. 

To be clear, the above pattern was Model/View/ViewModel before me and my team corrupted it.  As we started building actual UI using Avalon we found we need ValueConverters and Commands to complete our implementations, and we decided to fit them into the pre-existing pattern.  Obviously they aren't part of the Model, probably they shouldn't go in the View, so we ended up declaring them part of the ViewModel.  A better description of the actual pattern we use would be:

Data Bound Model/View/ViewState/ValueConversion/Command pattern with Avalon specific details

Given a Model consisting of a CLR class, XML schema, Web Service or relational data, create a View using XAML that contains the actual controls, styles and templates of the UI, a class or set of classes that abstract the state of the view (ViewState), define ValueConverters for Model types that do not map easily to the UI and Commands for operations on the Model or ViewState.  Then use Avalon's data binding functionality to declaratively (in the XAML) wire the View to the ViewState and/or directly to the Model.

But that definition still leaves out the heirarchical nature of the Model/View/ViewModel pattern as we practice it.  There is another pattern called Presentation/Abstraction/Controller that I find more generally useful than Model/View/Controller.  It is subtle, and can be used even for non-UI architectures if you consider the Presentation to be an API rather than an actual user interface.  Basically PAC divides an architecture up into Agents (components, sub-systems) that each consist of a Presentation, Abstraction and Controller.  The Presentation provides the visible behavior of the agent whether that is a UI or a public interface for another part of the program to use.  The Abstraction is the Model as in MVC or other definitions, and the Controller's job is to coordinate the Presentation and the Abstraction.  For example, if the Model changes, the Controller is generally responsible for notifying the Presentation and vice versa.  Higher level agents talk to lower level agents through their Presentation layers.  Compared to Model/View/Controller, Presentation is the View, Abstraction is the Model and Controller reuses the term from MVC, but gives it a completely different meaning (sigh). 

Presentation/Abstraction/Controller actually maps to Model/View/ViewModel quite well.  So another definition would be:

DataBound GUI Presentation/Abstraction/Controller pattern

Given an Abstraction consisting of a class or data source, create a Presentation using a declarative format consisting of the actual GUI components, supplemented with abstract view state, and use a general data binding engine supplemented with ValueConverters and Commands as the Controller to link the Presentation to the Abstraction. 

Confused yet?  At this point the pragmatic hacker returns the computer scientist to the dungeon and gets down to brass tacks.  Our definition of Model/View/ViewModel is heavily motivated by using a tool (Sparkle obviously) to create and edit the view.  As I've mentioned before these tools (Sparkle :-)) best understand XAML.  So what we are really trying to do is divide up the problem between a Model in OO code, and a View declared in XAML, and the ViewModel is the dumping ground for whatever doesn't fit (let's be honest). 

Model/XAML/ModelAdapter pattern

Given a Model, create a UI for the Model in XAML, then data bind the XAML to the Model, with additional ValueConverters, Commands and ViewState as neccessary defined in a ModelAdapter.

The above is a taste of why I didn't want to go into the semantic details of the pattern during my PDC talk.  I also hope it fleshes out the Model/View/ViewModel pattern and makes it a little easier to understand.

One final mutter from my inner computer scientist: 

As I mentioned in my introduction, another formal criticism of Model/View/ViewModel is the lack of the Controller from MVC in the pattern.  The role of the Controller is to map user input (keystrokes, mouse actions) to the View and/or Model.  In Smalltalk you actually implement a Model class, a View class and a Controller class.  In most modern GUI development environments the Controller is actually implemented by the system.  Input events are routed to the various controls and mapped into events (Click, MouseDown) that handle the interaction.  At most the developer provides a table of keyboard shortcuts, but isn't actually responsible for the input mapping of Controller.  This then is my reply to that criticism:  Controller is still there, it is just implemented by the system and is of little practical interest in the pattern.  You are however perfectly welcome to think of the Model/View/Controller/ViewModel pattern if you prefer.

Comments