This is a follow up to my post on the Model View Presenter pattern with ASP.Net to address or clarify some of comments from the earlier post. I apologize for being so slow in answering the comments.
The Presenter is a GoF Mediator
Carlton commented that the Presenter class looks a lot like the Gang of Four "Mediator" pattern. He's absolutely right. Something to keep in mind when learning to apply design patterns is that a class or group of classes can easily be an instance of more than one pattern. In this case "Presenter" or "Controller" is just a more specific name for a class within a larger structural pattern.
Unit Tests Aren't Enough
MVP certainly makes unit testing easier, but don't forget to create white box integration tests as well. The unit tests should remove most of the problems, but only integration tests will reveal all of the defects in the code. Conventional wisdom says to write automated tests just under the skin of the user interface. Our experience with writing integrated FitNesse tests against the Presenter classes with live services but a stubbed view has been positive. You could also write integrated tests that use the real view and the Presenter but mock or stub the backend to fully test the presentation. Sooner or later you'll have to do a full end-to-end test, but I think it's vastly easier to diagnose and fix defects with smaller white box tests first.
Exposing the Model
Harris Boyce's colleague didn't like the view class having a reference to the actual Model (WorkItem) object in the following:
// Abstracted interface of the WorkItem View screen
public interface IWorkItemView
{
// Packs the user input into a new WorkItem class
WorkItem WorkItem{get;}
// Sets the values in the dropdown list for the WorkItem assigned to user
string[] AssignmentList {set;}
// Sets the values in the dropdown list for the WorkItem categories
string[] CategoryList {set;}
}
It's a valid objection, but let me try to clarify what I was worried about here. One of the ways to bog down with the MVP pattern is to make the view interface way too chatty with far too many getters and setters like this:
public interface IWorkItemView
{
string AssignedTo {get; set;}
string Category {get; set;}
string Description {get; set;}
string Priority {get; set;}
string Comments {get; set;}
/* More properties */
}
In this case the Presenter starts to become way too busy with pushing data back and forth between the view getter/setters and the Model objects. One of the strengths of .Net user interface development is the strong support for databinding. I know it's far from perfect, but it still saves some grunt work and frees the Presenter up to concentrate on flow logic. I can understand some apprehension about exposing a Domain Model object to a presentation class, so here's some alternatives to consider:
- The "Model" doesn't have to be a Domain Model class. You can create a custom Data Transfer Object (very appropriate if there is a web service in the mix) or even a lowly DataSet to be the dedicated "Model" and keep your business logic fully encapsulated from the presentation layer. We actually create DataSet's from our domain classes for this very purpose.
- I'm not entirely sold on it, but take a look at Martin Fowler's Presentation Model pattern as an alternative to MVP. I've used it successfully for complex TreeView operations and the typical "sort/filter/page" operations in every grid reporting page. I'll post more on that soon.
- You can happily use more than one Presenter per View. One Presenter might handle the low level mapping from the Model to the View and input validation while a second Presenter deals with the flow logic and the backend services.
Constructing Deep Object Graphs
Michael asked how we handle the creation and assembly of deep dependency chains (but he answered his own question). The presenter needs a reference to the view and the backend services, which probably have dependencies of their own. Here's a representative case. A Presenter class has a dependency on IView, IUserInformationStore, and IServiceFacade. The concrete class ServiceFacade that implements IServiceFacade has its own dependency on IDomainRepository.
public class Presenter
{
private readonly IView _view;
private readonly IUserInformationStore _userInformationStore;
private readonly IServiceFacade _service;
public Presenter(IView view, IUserInformationStore userInformationStore, IServiceFacade service)
{
_view = view;
_userInformationStore = userInformationStore;
_service = service;
}
}
public class UserInformationStore : IUserInformationStore
{
public UserInformationStore(string domainControllerName){}
}
public class ServiceFacade : IServiceFacade
{
private readonly IDomainRepository _repository;
public ServiceFacade(IDomainRepository repository)
{
_repository = repository;
}
}
public class DomainRepository : IDomainRepository
{
private readonly IDataSession _session;
public DomainRepository(IDataSession session)
{
_session = session;
}
}
public class DataSession : IDataSession
{
public DataSession(string connectionString)
{
}
}
I'm a huge fan of using Inversion of Control/Dependency Injection to promote looser coupling and better testability, but the responsibility of attaching dependencies has to go somewhere. We obviously don't want the ASPX page that creates the Presenter class be responsible for creating the entire object graph of dependencies like the sample below.
public class View : Page, IView
{
private Presenter _presenter;
public View()
{
// Absurd example
_presenter = new Presenter(this,
new UserInformationStore("TheNameOfTheDomainController"),
new ServiceFacade(
new DomainRepository(
new DataSession("a connection string"))));
}
}
I would suggest one of two solutions. The simpler, but less powerful solution is to use "greedy" testing constructors to attach mock objects in tests, but use a default no argument constructor when a class is created in production code.
public class Presenter
{
private readonly IView _view;
private readonly IUserInformationStore _userInformationStore;
private readonly IServiceFacade _service;
// "Greedy" unit testing constructor only!
public Presenter(IView view, IUserInformationStore userInformationStore, IServiceFacade service)
{
_view = view;
_userInformationStore = userInformationStore;
_service = service;
}
// Default constructor
public Presenter(IView view) : this(view, new UserInformationStore(), new ServiceFacade())
{
}
}
The second and more powerful solution is to use a Dependency Injection tool like StructureMap to create the object graphs and attach all of the dependencies. My colleagues frequently snicker that my answer to most design challenges is to use StructureMap, but this case is exactly what I built it for.
public class View : Page, IView
{
private Presenter _presenter;
public View()
{
_presenter = new Presenter(this);
}
}
public class Presenter
{
private readonly IView _view;
private readonly IUserInformationStore _userInformationStore;
private readonly IServiceFacade _service;
// "Greedy" unit testing constructor only!
public Presenter(IView view, IUserInformationStore userInformationStore, IServiceFacade service)
{
_view = view;
_userInformationStore = userInformationStore;
_service = service;
}
// Default constructor
public Presenter(IView view)
{
_view = view;
// Use StructureMap to create the object graphs for UserInformationStore and ServiceFacade
// with all of the necessary connection string and domain controller configuration
_userInformationStore = (IUserInformationStore) ObjectFactory.GetInstance(typeof(IUserInformationStore));
_service = (IServiceFacade) ObjectFactory.GetInstance(typeof(IServiceFacade));
}
}
Using StructureMap to wire up the dependencies allows for far more flexibility to mix and match the application configuration. For example, by my team uses StructureMap to switch our web application from using certificates for authentication to a stubbed security mode on our developer boxes by a simple change in the StructureMap configuration. We also use StructureMap to turn encryption off for easier debugging, stubbing the database, and switching from an installation where the application runs on two physical tiers in production mode to a testing mode where the entire application runs inside a single AppDomain for easier debugging and faster tests. Here's a more detailed example of using StructureMap. I've mostly used StructureMap for creating pluggable MVP architectures with WinForms applications, but I don't see why it wouldn't be applicable for web applications as well.
Posted
02-16-2006 12:15 AM
by
Jeremy D. Miller