Tuesday, June 17, 2008
Posted on Tuesday, June 17, 2008 10:18:00 AM (Mountain Daylight Time, UTC-06:00)  Comments [3] | 
Categories: .NET | Unit Testing

Based on the outcome of the Developer Survey, I thought I' put together some posts on the common patterns referenced in some of the questions, starting with the Model View Controller (MVC) family.

There has been plenty written about MVC on the formal side of things - here are two good sources of information - Wikipedia MVC entry,  and some thoughts from Martin Fowler.

The Basics

The MVC family of patterns are about separating the presentation of data from the business logic surrounding the management of the data. There are many other patterns based on the key MVC ideas - Passive View, Supervising Controller, Presentation Model and Separated Presentation are 4 such patterns. The differences are subtle, and all share they key idea of separating the presentation logic from the business logic, from the data itself. It's worth noting that the opposite of MVC family is called "Autonomous View" in which all the logic is in the form itself - so if this is how you are developing, now you can use a fancy name for it!

Since MVC was one of the first patterns that dealt with this separation, we'll start with it, and look at the three components...

Model:

The model is the data and it's usually a class. So in GIS-land, it could be a "Parcel" class, which may have a Geometry property. The important part is that this class has no dependencies on the presentation layer, or the controller. It's just a dumb data container. Typically this class will implement one or more interfaces, which the View and Controller will utilize. Interfaces are used a lot in these patterns, so here's a link to an article that compares Abstract Classes and Interfaces that can serve as a refresher if needed.

View:

As the name suggests the View is responsible for organizing and presenting the Model. Usually this means showing it visually on a form. In order to remove dependencies between the View and the Model, the View will interact with an interface, instead of a specific view class - i.e. IParcelFeature instead of "Parcel". The view typically raises events when the user makes changes in the interface, and exposes properties allowing the Controller to "see" the model or other state of the view. The View does not make any changes to the Model itself (in some  MV* patterns does not actually have visibility of the model - the controller sets all the control values directly, and responds to all control events). Again, the events and properties of the View are defined via an interface.

Controller:

The name conveys things pretty well - this is the business logic component that controls what actually happens to the model when an event occurs in the view. There may be multiple controllers that can work with a given class, or multiple model classes that can work with a given controller. This is where the Interfaces on the Models come in. If the Controller's "contract" is based on an Interface (IParcelFeature), then we can use the Controller with any class that implements the interface. Similarly, the Controller also implements an Interface, which is it's contract with the View.

Pulling it to all together:

In MVC, the Controller gets a model and a View. It then passed the required data to the View which renders the UI. When the user interacts with the UI (i.e. clicks a button), the View raises events, which the Controller responds to by making required changes to the Model, which is then used to update the View.

Why Bother?

So - this is nice, but why separate things out like this? There are a number of reasons. First is known as Single Responsibility. This concept basically says that a class should have one purpose, and anything not connected with that purpose should be in a separate class. In regards to the subject at hand, it would tell you to avoid mixing presentation code, with business logic because they are not actually related.

Another reason for MVC is re-use. If you model your application like this, you can plug and play the components - particularly the View. So, suppose you need to create a business application that has a windows forms client for some functionality, a web client for some other functions, and a web service API. You can build out a model, a controller, and three views - one for the windows forms application, one for a web form, and a third (more limited one) for the web service.

A final (and biggest) reason is testability. If you keep the Model (data) separate from the View (User Interface) which is separate from the Controller (business logic), writing unit tests becomes much easier. If you've implemented interfaces as the contract between the components, you can test the components individually.

The Pain of Testing

Without getting too deep into unit testing, the basic idea is to have a test harness which spins up various classes in your code, and executes methods. This gets complicated because our classes have dependencies on data - stored in databases, shape files, whatever. If we want to re-run our tests frequently, we want to be sure our test data is static - otherwise the tests break, and that defeats the purpose. So - the next best option is to create "fake" static data in our tests. The brute force method would be to create specific test classes which implement the required interfaces (ISomeView, ISomeModel etc), but just contain enough logic to complete the test. Depending on the test you would have an invalid geometry, invalid PIN number or a myriad of other conditions. This would allow you to create test "Parcels" in your unit test harness, that you could then pass to the Controller to test it's methods, without actually connecting to a shape file. You could also create a class that implemented the View interface, and use that to raise events and ensure that the Controller reacted correctly. Essentially you can write unit tests for everything but the "click" event handler in the UI layer. Since that code was likely written for you by the IDE, and it's super basic, it's not a really high priority for testing.

Easing the Pain of Testing: Dynamic Mocking

Of course that's a lot of work, and developers hate extra work, so tools were created to help out. While this is beyond the scope of this post, I'm talking about "mocking" frameworks, such as NMock, RhinoMocks or TypeMock. These frameworks can create a "test dummy" class that implements a specific  interface right in the test definition. You basically "record" some values into the Mock, then pass it into a method, and it can validate behavior.

Of course you still need to fill up the dynamically mocked Interface with valid data - and the geometries are likely the scariest thing to start with. So that's where I started a few years ago.

I can haz code?

I don't have anything lying around that's easily sharable, but I did post a sample about Supervising Controller code about a year ago. The code is in VB.NET, with a Visual Studio 2005 solution, and is oriented towards showing how to create a plug-in framework, while leveraging Supervising Controller, so that's the gist of the article. It also does not have any tests, but I'll be posting more leveraging MVC patterns and unit testing in the future.

Plug-in Framework & Supervising Controller Example (Article - read this before digging into the code)

Download Sample Code

Summary

The idea here was to introduce the main idea of the MVC pattern - separation of behavior logic from presentation logic, usually to facilitate testing. The actual implementation of this idea can take many flavors, and can vary in complexity, but the underlying concept is the same.

Wednesday, June 18, 2008 8:05:47 AM (Mountain Daylight Time, UTC-06:00)
Excellent post.

Actually just the information and examples I needed to help coalesce a few "How the heck am I going to do that" thoughts into something tangible.
MR
Thursday, June 19, 2008 11:46:33 PM (Mountain Daylight Time, UTC-06:00)
Dave, as usual you rock. Excellent post and must needed for this hour. There were several articles in internet but your explanation in GIS context is damn good. Do post such articles.
Sunday, June 22, 2008 10:07:33 AM (Mountain Daylight Time, UTC-06:00)
Hi

Firstly, thanks for an interesting article. Now, there appears to be a problem with the download link for the code (page not found) - any chance of updating the link?

Cheers
Martin
Martin
Comments are closed.