An Apology, and Topics Still To Be Covered
Let me start by apologizing to all the CAB/SCSF fans (?) out there who have been asking me to continue the series of posts. I know it’s been a while since I wrote about the CAB/SCSF (I got diverted by FpML as you can see). However the series of articles is not yet finished and I will be sporadically posting on the topics many of you have been asking about. If there’s interest I may look at how to use some of the other application blocks with the CAB as well.
If there are specific things you would like to see covered please leave a comment.
Having said that, our CAB project is live and working well, and we are moving onto other issues, so my enthusiasm for spending my Saturday mornings writing about CAB/SCSF has waned somewhat.
Part 21 of this series of articles briefly discussed foundational modules and the way constants are handled in an SCSF project.
Part 23 of this series of articles will examine another Smart Client Software Factory Guidance Automation pattern, the Model-View-Presenter.
By way of preparation for part 23, this article discusses the problems with putting code directly behind screens in smart client applications, and a traditional solution to this, which is Model-View-Controller.
The Basic Problem
When writing user interface code it’s quite easy to put too much logic in the class that draws a form or user control.
In badly-designed .Net applications, for example, it’s not uncommon to see classes behind user controls of several thousand lines of code (excluding the automatically generated code). We may have code that handles events, updates the data on the screen, caches and refreshes data, updates the state of user interface controls based on the data or user input, colors parts of the screen based on the data, binds data to controls in complex ways and so on.
With all this code in one class it can be difficult to work out what is going on. The code becomes hard to maintain.
Clearly if we have a lot of code doing different things in a class we are violating the single responsibility principle. We should be looking to refactor in such a way that we have a number of classes, each with a clear role and tidy interfaces into the other classes.
The difficulty with this is that it isn’t necessarily easy to see how to break up this sort of code in a logical way. There are a number of user interface patterns that address this. The granddaddy of them all is Model-View-Controller (MVC), although as we shall see this is not a pattern you are likely to see in a .Net application, at least in its original pure form.
Where to Cache the Data
It’s arguable that the main problem Model-View-Controller solves is that of data being cached directly behind a screen and not elsewhere in the application. Again this is a common problem in poorly written smart client applications.
Suppose we have a screen that displays data that has been retrieved into a series of textboxes directly from a middle-tier service. This data is loaded into the textboxes the first time it is displayed, retained in the textboxes but not actually stored in any variables anywhere. There may be a ‘refresh’ button that lets us reload the data from the service. However, the values in the textboxes will not automatically update if there’s a change server-side.
Suppose we now want to add another screen that uses that data in some calculations. Where does the second screen get the data from? If it goes back to middle-tier service the data may be out of sync with the data displayed in the screen. Should it go to the screen itself?
The traditional solution to this problem is to use the Model-View-Controller pattern (MVC).
MVC dictates that we have three classes for user interface display:
- A Model class contains the data to be displayed. This can be a simple data cache, although it may be a full domain model (classes containing data plus associated business functionality).
- A View class that contains the screen code. This would be a user control or form in .Net.
- A Controller class that provides the glue between the Model and the View. This class is a little more difficult to understand than the other two. The idea is that the Controller handles all user input, which means events raised in response to user actions (e.g. clicking a button, changing a value in a textbox etc.). It then updates the Model as necessary.
Note that the Controller does not update the View. It updates the Model. If necessary the Model will then raise an event saying that it has changed. The View will subscribe to this event and redraw itself, getting the new data directly from the Model. Note that this is the Observer design pattern: the View(s) observe the Model.
In its purest form the View class should ONLY contain code that draws to the screen: it shouldn’t contain any event handling code for user events, all of which should be in the Controller. This isn’t the usual pattern with a .Net user control of course, where your event handlers are normally in the code behind the user control (the View). It’s possible to set up the event handlers actually in the Controller, or to just immediately call the Controller from the View in the event handler.
Class Interaction in Model-View-Controller
These classes should have references to each other as shown in the diagram below:
The solid arrows above represent references between classes through which direct method calls can be made.
The View class will have a reference to the Model to allow it to directly request the data it needs to display (A).
The Controller will have a reference to the Model because it will need to manipulate it in response to user actions (B). The Controller also has a reference to the View because it may need to retrieve values that have been changed in order to manipulate the Model in an appropriate way (C).
Note however that the Controller will not update the View directly. The View itself will go to the Model to actually get the data.
The dotted lines above represent an indirect relationship where events can be raised from one class into the other. So the Model may raise events when its data changes, which the View will sink and update any display depending on that data (D).
Note that the Model has no direct reference to the other two classes: it is independent of any Views or Controllers.
Use Case Examples
It can be difficult to understand exactly how this fits together when we see it for the first time. In particular what the Controller does can be confusing.
To clarify this here is a use case example:
- A user interacts with a screen displaying data (which is the View). Suppose that they change some base data and hit an Update button. Further suppose that they expect some derived data on the screen to change as a result of this action.
- The click event for the Update button gets raised in the Controller. A parameter to the event may be the data that needs to be saved (otherwise the Controller can go back to the View to get it). The Controller takes the changed data and updates the Model for the changes. This will include updating any derived data.
- The Model raises an event that it has changed.
- The View will sink the event and redraw itself based on the new data. This means that the correct derived data will now be displayed.
- The system waits for further user input when the cycle above will be repeated.
Solving the Data Caching Problem
MVC gives a solution to the data cache problem outlined above. We have separated the data for our screen into a Model class. Any other screen that needs to display the same data can access the same Model class (using its own Controller). The textboxes on the screens are effectively just views of this data whenever they are displayed.
As a result if the data changes it should only need to be changed in the one place (the Model) for the individual screens to be able to use it. What’s more, because the Model raises an event when the data changes, ALL we need do is change the data in the Model and all the screens will automatically update. If we change a value in a textbox on one screen it should update on any other screens via the process described in ‘Use Case Examples’ above.
Note that this means that one Model can be associated with multiple Views and Controllers. However, it is usual to have a one-to-one mapping between Views and Controllers: each View will have its own unique Controller class.
The Model-View-Controller pattern described in this article is one that could be used with modern programming languages such as .Net. The description here is what is usually meant by ‘MVC’ these days. However, MVC as described here differs slightly from the original pure MVC design.
MVC originated with early Smalltalk which was a very different language from modern user interface languages like .Net or Java. For a detailed look at the history of MVC (and of MVP) see Martin Fowler’s article ‘GUI Architectures’.
In the original Smalltalk design every single control (textbox, button, etc.) would have its OWN View and Controller class, and the screen they were on would have a top-level View and Controller that would handle putting them all together. This was because Smalltalk didn’t have draggable user interface controls in the way we do today, so writing a user interface was a somewhat different proposition.
Clearly we could do this in a modern programming language such as .Net, but it would arguably make our code less clear rather than clearer.
Active and Passive Model-View-Controller
MVC as described here is ‘active’ MVC. This means that, as we have seen, if the Model changes all Views will be updated automatically via the eventing mechanism.
It is also possible to set up ‘passive’ MVC. This is a slightly simpler pattern where the Model does not raise an event when it changes, and consequently the View cannot update itself ‘automatically’. Instead the View is updated when the user next requests it (by pressing a ‘Refresh’ button for instance). At this time the View will go to the Model and get the new data.
Advantages of Model-View-Controller
MVC obviously has the advantage that for complex screen logic we get clearer code, which should be easier to maintain. In theory this pattern also makes it easier for us to replace the user interface itself, since only screen logic is in the screen.
MVC also provides an easy way of allowing multiple views of the same data to always be synchronized (via the event mechanism).
Disadvantages of Model-View-Controller
Of course, there’s one obvious argument against using MVC for every single screen in a system: it’s a fairly complex pattern, and for very simple screens may simply mean we create three classes where we only need one. Also you clearly have to learn the pattern to be able to use it effectively, which is an overhead.
MVC also suffers from the same problem that all eventing mechanisms have: it can be hard to debug. When the Model fires an event saying it has changed it may not be immediately obvious where this event is being sunk, and finding out in order to debug the code may be tricky.
However, the biggest problem with MVC is that it doesn’t actually solve all of the issues involved in the original problem described in this article. There are multiple reasons why we might have too much code behind our screens, and for some of these it’s not clear how MVC helps us. For this we may need a slightly different pattern, which might be the Model-View-Presenter pattern the SCSF gives us.
This will be discussed at greater length in part 23 of this series of articles.
Model-View-Controller is a pattern that can be used to structure the code in a user interface in a logical way, and prevent too much code being in the screen class itself. In particular it splits the data for a screen into a separate class.
However, it doesn’t solve all our user interface coding problems as we shall see in the part 23 of this series of articles. For this reason it’s not all that common in modern .Net applications.
Martin Fowler on MVC and MVP
MVC was originally designed for Smalltalk
Microsoft on MVC