When last we left D’Artagnan he had just concluded a successful duel with a number of screen concerns by dividing them with a masterful usage of the Humble Dialog Box. As D’Artagnan strives to regain his breathe, he’s heartened by the appearance of his three doughty companions Athos, Aramis, and Porthos. As the four friends sit down in a shady spot besides the road for a fine meal of delicacies (unknowingly donated by a noblewoman of Athos’ acquaintance), D’Artagnan describes their predicament when they manage to corner the Lady de Winter and her host of minions.
A Shipping Screen
Let’s imagine that you need to build a little screen to allow a user to configure the shipping options for some sort of electronic order that looks something like this screenshot below:
The user first needs to select the state or province (since this was for a talk in Canada, I thought it would be nice to let our Canuck neighbors to the north use the app too) that is the destination of the shipment. Not every shipping vendor can ship to every destination, so we should modify the contents of the shipping vendor dropdown to reflect the destination. Likewise, a change in the selected shipping vendor will also cascade down to the list of shipping options (think Overnight, 2 business day, parcel, etc.). Some, but not all, shipping options allow the customer to purchase insurance in case of loss or require a signature at the destination for guaranteed delivery. Finally, the cost of the shipment should be recalculated and displayed on the screen anytime the shipping selections are changed.
I can spot a couple different responsibilities in just that paragraph, plus a couple more down the line when it’s time to actually submit the shipment. Even this little screen has enough complexity in it that I wouldn’t want to bundle all of the responsibilities into a single autonomous view. So let’s enumerate just the responsibilities from that paragraph and start thinking about how to assign these responsibilities to different classes.
- Display and capture the currently selected shipment options
- Respond to user events like selecting a value in the ComboBox’s
- Fetching the list of states
- Fetching the list of shipping vendors for a given state or province
- Fetching the list of options for a given state/province and vendor
- Changing the dropdown lists
- Enabling and disabling the checkbox’s for purchasing insurance and requiring a signature
- Calculate the shipping cost for the selected options
- Update the cost textbox whenever the shipment options change
That’s a fair amount of behavior and business logic for one little screen. We know that we probably want to employ some sort of Humble View approach to split out some of the responsibilities from the Form class and into POCO classes that are easier to test. I’m going to show three sample solutions of the shipping screen, each using a bit different approach to assigning responsibilities.
Interlude
The four friends mull over the looming fight with the forces of Lady de Winter as they finish off a fine cheese produced from the endless saddlebags of Aramis’s manservant. D’Artagnan poses this question to the older musketeers: “When I’m beset by a multitude of concerns in a single screen, what technique should I use to best each concern in turn?” The three older musketeers ponder the question raised by their younger companion. Finally, Athos speaks up. “I would not concern myself with the simpler responsibilities of a screen. I would first seek to eliminate the more complicated screen responsibilities by employing…”
The Supervising Controller
The goal of the Supervising Controller variant of Model View Presenter is to remove the more complicated screen scenarios that deserve more unit testing attention out of the view and into a separate Presenter class. The Supervising Controller strategy takes advantage of the data binding support in WinForms for simple screen synchronization. To that end, we’ll create a simple class called Shipment that will be our Model in the MVP triad.
public class Shipment : INotifyPropertyChanged
{
private string _stateOrProvince;
private string _vendor;
private string _shippingOption;
private bool _purchaseInsurance;
private bool _requireSignature;
private double _cost;
public string StateOrProvince
{
get { return _stateOrProvince; }
set
{
_stateOrProvince = value;
fireChanged(“StateOrProvince”);
}
}
public string Vendor
{
get { return _vendor; }
set
{
_vendor = value;
fireChanged(“Vendor”);
}
}
// And the rest of the properties…
}
The View itself simply has a setter that takes in a Shipment object and starts up the data binding.
public Shipment Shipment
{
set
{
// start up the data binding stuff
}
}
I’ll talk about options for the Model in depth in a later post, but for now, let’s say that Shipment is just a dumb batch of getters and setters. Since I can’t stand writing INotifyPropertyChanged interface implementations by hand, you’ll probably want to codegen these Model classes — again giving me even more reasons to keep the Shipment class dumb.
We’ve taken care of the actual presentation of the shipment data, so let’s move on to more responsibilities. There’s no possible way that a screen should know how to calculate the shipping costs, and probably shouldn’t know how to fetch the data for the dropdown selections. Deciding whether or not a shipper and shipping option allows a user to purchase insurance or require a signature on receipt is business logic for the real domain logic classes, not screen logic that belongs in the screen. Let’s not particularly worry about how this stuff is implemented right now. Let’s just define an interface for all of this functionality (and a Data Transfer Object as well).
public class DeliveryOptions
{
private bool _purchaseInsuranceEnabled;
private bool _requireSignatureEnabled;
public bool PurchaseInsuranceEnabled
{
get { return _purchaseInsuranceEnabled; }
set { _purchaseInsuranceEnabled = value; }
}
public bool RequireSignatureEnabled
{
get { return _requireSignatureEnabled; }
set { _requireSignatureEnabled = value; }
}
}
public interface IShippingService
{
string[] GetLocations();
string[] GetShippingVendorsForLocation(string location);
string[] GetShippingOptions(Shipment shipment);
void CalculateCost(Shipment shipment);
DeliveryOptions GetDeliveryOptions(Shipment shipment);
}
The IShippingService interface works on our Shipment class that we’re binding to in the actual screen, as opposed to exposing primitive arguments. When I was writing the sample code it seemed to me to be a simple way of interacting with the service because it cuts down on any data transformations between screen and service. The CalculateCost(Shipment) method would also write the cost back to the Shipment object, cascading a corresponding change to the screen as the data binding updates the screen based on changes to Shipment. It’s probably worth noting that this IShippingService could just be a Facade class over the business logic specifically created for easier consumption by the user interface. In the next chapter I’ll take a different approach that exposes the underlying Domain Model classes for the shipping system.
At this point we’re largely left with just responsibilities for changing the dropdown options and mediating between the View and the IShippingService. That’s where the ShippingScreenPresenter finally comes in to provide the missing functionality that goes beyond simple data binding, as well as coordinating user actions with the IShippingService.
public class ShippingScreenPresenter
{
private readonly IShippingScreen _view;
private readonly IShippingService _service;
private Shipment _shipment;
public ShippingScreenPresenter(IShippingScreen view, IShippingService service)
{
_view = view;
_service = service;
_shipment = new Shipment();
// Since we’re got the INotifyPropertyChanged interface on Shipment,
// we might as well use it to trigger updates to the Cost
_shipment.PropertyChanged += new PropertyChangedEventHandler(_shipment_PropertyChanged);
}
private void _shipment_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
_service.CalculateCost(_shipment);
}
public Shipment Shipment
{
get { return _shipment; }
}
public void Start()
{
_view.Shipment = _shipment;
}
// React to the user selecting a new destination
public void LocationChanged()
{
_view.Vendors = _service.GetShippingVendorsForLocation(_shipment.StateOrProvince);
}
// React to the user changing the Vendor
public void VendorChanged()
{
_view.ShippingOptions = _service.GetShippingOptions(_shipment);
}
// React to the user changing the Shipping Option
public void ShippingOptionChanged()
{
DeliveryOptions options = _service.GetDeliveryOptions(_shipment);
_view.InsuranceEnabled = options.PurchaseInsuranceEnabled;
_view.SignatureEnabled = options.RequireSignatureEnabled;
}
}
Since I’ve been claiming that this style of user interface structure improves the testability of the screen as a whole, let’s take a look at what the unit tests might look like. Here’s the unit test for correctly enabling or disabling the insurance and signature checkbox’s on the shipping screen after the shipping option changes:
[Test]
public void EnableOrDisableTheInsuranceAndSignatureCheckboxesWhenShippingOptionChanges()
{
MockRepository mocks = new MockRepository();
IShippingService service = mocks.CreateMock<IShippingService>();
IShippingScreen screen = mocks.CreateMock<IShippingScreen>();
ShippingScreenPresenter presenter = new ShippingScreenPresenter(screen, service);
// Setting up the expected set of Delivery options
DeliveryOptions deliveryOptions = new DeliveryOptions();
deliveryOptions.PurchaseInsuranceEnabled = true;
deliveryOptions.RequireSignatureEnabled = false;
// Set up the expectations for coordinating both
// the service and the view
Expect.Call(service.GetDeliveryOptions(presenter.Shipment))
.Return(deliveryOptions);
screen.InsuranceEnabled = deliveryOptions.PurchaseInsuranceEnabled;
screen.SignatureEnabled = deliveryOptions.RequireSignatureEnabled;
// Perform the work and check the expectations
mocks.ReplayAll();
presenter.ShippingOptionChanged();
mocks.VerifyAll();
}
The first thing you might notice is that this is definitely an interaction based unit test. That’s not surprising since one of the primary duties of a Presenter is to mediate between the services and view. In development with Test Driven Development / Behavior Driven Development, it’s often advantageous to separate the responsibility for performing an action away from the decision to perform that action. In this case, the real View enables the insurance and signature checkboxes when the Supervising Presenter tells it to enable or disable the checkboxes. In other words, the Supervising Presenter is the mostly immobile queen bee, and the View is the mobile, but relatively brainless, worker bee. In unit tests like the one above, we’re largely testing that the Presenter is sending the correct signals to the View and service interfaces.
For another example, here’s a unit test for recalculating the shipment cost as the selected shipping options change:
[Test]
public void UpdateTheCostWheneverTheShipmentChanges()
{
MockRepository mocks = new MockRepository();
IShippingService service = mocks.CreateMock<IShippingService>();
IShippingScreen screen = mocks.CreateMock<IShippingScreen>();
ShippingScreenPresenter presenter = new ShippingScreenPresenter(screen, service);
service.CalculateCost(presenter.Shipment);
mocks.ReplayAll();
presenter.Shipment.Vendor = “a different vendor”;
mocks.VerifyAll();
}
That wasn’t that bad, now was it? But wait, you ask. Where’s the actual logic for calculating the shipment cost? For right now I’m just worried about the wiring of the screen itself. Yes, this unit test covers very little ground, and it’s not a “real” test, but I’ve created some level of trust that this particular linkage in the code does work. I can now turn my back on the screen itself and test the actual ShippingService by simply pushing in Shipment objects and checking that the Cost field is correctly updated. Not one single line of the shipping screen code needs to be present for any of that logic to be tested. I hope it’s needless to say that there really shouldn’t be any leakage of domain logic into the screen code. Real domain logic in views is just about the fastest way possible to create an utterly unmaintainable system.
Interlude
The four friends pondered Athos’s solution in quiet contemplation in the state of contentment that only follows a fine meal. “Wait,” exclaims D’Artagnan, “You haven’t told us how the View talks to the Presenter, and who creates who! How should this work?” Athos simply shakes his head and says “it’s a long ride to Dunkirk, we’ll talk more of this on the road.” (as in, I’ll get there, just give me a couple more chapters – Jeremy).
How I Prefer to Work
Personally, I wouldn’t order the work quite the way I showed above. I like to start with either a screen mockup or maybe even the actual view. If you start with the actual concrete view class, don’t wire it up yet, and whatever you do, don’t automatically implement the intended “IView” interface until you’re done with the Presenter. Sometimes I’ll start by taking some notes or sketching out some UML or CRC models about the screen scenarios. My next step is to encode the behavioral aspect of the screen in the Presenter. I use RhinoMocks in place of both the View and whatever service interfaces the Presenter needs. As much as possible, I like to define the methods and interactions of the Presenter with the services and view in a unit test body and just let ReSharper generate the methods on the dependencies. Since we’re strictly dealing with interfaces here, we don’t have to break our flow with the Presenter to implement the services and view just yet. As soon as the Presenter itself is fully fleshed out, I implement the IView interface on the real View and create an implementation for any of the service dependencies.
This probably isn’t realistic for complex screens, but at least on simple screens it should become quite ordinary for a screen to just work the first time it’s run in the UI — assuming you unit tested all of the pieces individually.
Summary
I’d bet that Supervising Controller is probably the most commonly used Humble View architecture, but I don’t have any figures at hand on that. It still allows you to use the design time support to layout controls and even configure the data binding with the visual tools (but we’re going to talk about options to data binding later). My current project is using Supervising Controller (sans data binding), and I think it’s largely where the sweet spot is. I will use the other options at times though, and there are a lot of different variations on each of the other Humble Views, so we’re not done yet.
Is this really worth doing? I say yes, otherwise I wouldn’t be doing it. Let me put it to you this way, on the first project I used the Humble View (Passive View actually) approach, we saw a strong correlation between the bug count per screen and the unit test coverage for the code on that screen. Screens that were driven with unit tests on the Presenter had many fewer behavioral bugs. I’m talking it even farther on my current project by adding NUnitForms tests on the View to Presenter interaction, plus Fit tests on the actual screen behavior.
If you’re not a fan of interaction based testing, and using mock objects gives you an allergic reaction, don’t worry. I’ve got stuff for you too coming up.
Conclusion
The four friends pondered the wisdom of Athos’ approach while drinking their way through the rest of the wine from D’Artagnan’s packs. Suddenly, mighty Porthos clears his throat and…
To be continued in Part #3 – The Passive View Pattern