Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Model View Presenter mutated: Observable View

Transgression

One of my crusades of the past year has been to break myself and my team of the habit of embedding domain logic into the code-behinds of our ASP.NET pages. For me, this is a habit that has carried over from the ASP 3.0 days of old. More recently, we have a nice domain layer that is persisted to the database using NHibernate. Classes are created that encapsulate specific functionality, whether through design or refactoring. We have even adopted Dependency Inversion through use of interfaces, which has allowed for unit testing of these classes. But many of our pages still contain too much logic about the process of how to perform a given task. This means a significant portion of code was not being tested.

Repentance

The first source to shed some light about how to extract this remaining logic from the pages was Martin Fowler's Patterns of Enterprise Application Architecture in which he covers the Model View Controller pattern. Much googling lead to my discovery of Model View Presenter. The most enlightening source on this was Jean Paul Boodhoo's Model View Presenter video on dnrTV. We began experimenting with MVP in our designs shortly afterwards.

Transcendence

Finally, extraction and unit-testability of the logic that once existed in our code-behind. But there was still something I didn't like about our implementation of MVP. The code-behind was still responsible for calling the correct method on the presenter whenever a control's event was raised. I felt like we were 90% there. Through experimentation, I eventually came up with what I call the "Observable View" pattern:

The interface:
interface IManageCustomerView
{
string Name { get; set; }
// etc...

void DisplayValidationErrorMessage(string errorMessage);
void DisplaySavedSuccessullyMessage();
// etc...

// The interesting part...
event EventHandler Initialize;
event EventHandler SaveCustomer;
event EventHandler DeleteCustomer;
}
The code behind:
class ManageCustomer : Page, IManageCustomerView
{
protected override void OnInit(EventArgs e)
{
new ManageCustomerViewObserver().Observe(this);

if (Initialize != null)
Initialize(this, EventArgs.Empty);
}

protected void OnSaveButtonClick(object sender, EventArgs e)
{
if (SaveCustomer != null)
SaveCustomer(this, EventArgs.Empty);
}

// etc.
}
The observer:
class ManageCustomerViewObserver
{
public void Observe(IManageCustomerView view)
{
view.Initialize += delegate
{
new ManageCustomerViewInitializationProcess(view).Execute();
};

// repeat for each event
}

}

(Note: The code above probably has typos... wanna fight about it?)

The key element is the use of events. This allows the view implementation to forget about observer after calling the Observe() method. The reference that keeps it from falling out of scope is the observer's subscription to the view's events. The .NET framework's event mechanism hides makes this slightly less than obvious. The events' presence in the interface also assist the implementation in hooking to the presenter observer correctly. The observer simply delegates the logic to a process class (one per event) which is an implementation of the Command pattern. The observer class only contains the Observe() method, which is simply glue code that doesn't warrant unit testing. Each process class can be tested independently or as a group. All in all, I'm pretty satisfied with this design. It does lead to a lot more class files in the project, but unit testing, the Single Responsibility Principle's biggest cheerleader demands most of this anyways.

 


Posted 10-03-2006 6:25 PM by Jim Bolla
Filed under: , ,

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)