ghytred.com


NewsLook for Outlook

Patterns and OO
MVC
The Web Site you seek
Cannot be located, so
Waste your time here

Contents

Model-View-Controller and some variants

The prime sources of information for this article are: Martin Fowler, Patterns of Enterprise Application Architecture, MSDN and subsequent pages and Object Arts, the creators of Dolphin Smalltalk.

Basic MVC

Although the title of this refers to MVC, it is really about different techniques for presenting web pages. Basic MVC came out of the Smalltalk world in the late 70's and appears to be pretty much built in to Smalltalk IDE's. Possibly the canonical description is by Steve Burbeck.
The static class diagram is simple:

In this diagram, Model represents domain classes such as Customer, Order, etc, none of which have any knowledge of how the data they supply will be presented. View and Controller both have knowledge of the Model; Controller additionally knows about the View.
A variant of MVC which is sometimes mentioned is MVP: Model-View-Presenter, in which the Presenter may be a composite of more atomic Presenters (e,g, textPresenter).
In a basic MVC there is usually a one-to-one relationship between views and controllers: each view has one controller and each controller uses one view. As a result of this, in many cases View and Controller are combined - ASP pages, WinForms forms, etc.

Extending basic MVC

Active Models

Basic MVC as described above employs a Passive Model: the model supplies data when asked for it and responds to requests to change that data. However if the model's data is changed somewhere else there is no way for the model to inform any current presentation of that data. Enter the Active Model. This employs the GoF Observer pattern where the Model exposes events which the View and/or the Controller can subscribe to.

Multiple Controllers

It is possible for a single view to have multiple controllers - one for Read/Write access, say, and one for read-only. This can be implemented by making the controllers GoF Strategies which are given to the View as appropriate.

Pros and Cons of basic MVC

Pros (these are mostly to do with the separation of the View from the Model):
  • Presentation and Model are fundamentally different and are developed with different concerns and different techniques.
  • Separating the Model from its presentation allows multiple representations to be developed.
  • Automated tests for a separate model are easy; automated tests for a UI are complex to set up and fragile. Separating the model from its presentation allows it to be tested relatively easily.
  • The Views can be changed without impacting the underlying Model or even the Controller.
  • Multiple views of the Model are easy: having a Web, a WinForm, a WAP Phone, and a character-based presentation have no impact on the model whatsoever.
Cons:
  • MVC introduces extra levels of indirection and thus complexity
  • Active Models introduce yet more complexity and can also, in extreme cases, flood a view with update events.
  • Basic MVC concentrates on the separation of the Model and the View; the controller is less important.
  • Controllers are likely to have a lot of duplicate code for tasks such as authentication, parsing a query string, reading and writing Session information, etc. This is inefficient.

Page Controller

This is a simple extension of basic MVC; the Controller is replaced by a Page Controller and the separation of the View and the Controller is emphasised. ASP.NET web forms follow this pattern, with the aspx file being the view and the code-behind being the Page Controller.
Generally there is one Page Controller for each page on the site.
This is the first step away from basic MVC to take when using dynamic pages rather than static.
A Page Controller has three basic responsibilities:
  • Decode the URL requested and extract any form data
  • Access the Model to retrieve or process the data requested. All HTTP data required should be extracted and passed to the Model so that the Model objects don't have any dependencies of their being an HTTP Context.
  • Determine which View is appropriate for the results and forward the Model information to it.
Note that these responsibilities apply as much to an initial page request as to a PostBack.
Looking at the responsibilities, it seems likely that some of the work on every Page Controller in a site is going to be duplicated. This duplication can be removed by either using helper classes (e.g. UrlDecoder) or by creating a BaseController class containing the commonality with GoF Template methods for the real Page Controllers to override and/or implement:
public class BaseController {
	protected void Page_Load(object sender, EventArgs e) {
		if (this.InvalidUrlForSomeReason) {
			transferToErrorPage(invalidUrlReasons);
		}
		DataSet ds = LoadData();
		if (HasNoData(ds)) {
			transferToErrorPage("No data matched request");
		}
		ApplyDomainLogic(ds);
		DataBind(ds);
	}
	protected virtual bool InvalidUrlForSomeReason() {
		// validate url
		// maybe set private string invalidUrlReason
	}
	protected abstract DataSet LoadData();
	protected virtual void ApplyDomainLogic(DataSet ds) {
		// do something with the data - add a calculated column etc
		// or just leave blank as here if no logic to apply
	}
}
			
The templated Page_Load method breaks down the request into a number of steps, each of which can be overridden by Page Controller implementations. The LoadData method in this example has to be implemented by each inheritor.
The DataBind method is properly part of the View (I am assuming that this system makes heavy use of data binding; other methods can be used by adjusting the template method appropriately).
There is nothing to prevent the base class - or its implementors - from using Helper classes.
This example is more likely to be called ListBaseController, and have matching DataEntryBaseControllers for example.

Pros and Cons of Page Controller

Pros:
  • Because each dynamic web page is handled by a specific Page Controller the controllers have limited scope and are thus relatively simple.
  • This pattern is built-in in ASP.NET and is thus easy to implement.
  • Controller Base classes reduce duplication and promote re-use
  • If Page Controller is not suitable for every page in the site it need not be used by every page in the site.
Cons:
  • In a site with complex dynamic navigation it is not always possible to abstract the navigation completely to a Base controller. The navigation thus becomes spread out over the site and difficult to maintain.
  • The Page Controllers are dependant on the Http context and are thus difficult to test. If they contain too much logic this decreases the testability of the system. The parts which do not depend on Http (or from which that dependence can be removed such as the ApplyDomainLogic method above) can be removed to separate classes but although these can then be tested separately it introduces another layer of indirection and thus complexity.

Front Controller

This is more complex than Page Controller. Microsoft list the forces which make it applicable as:
  • If common logic is duplicated in different views of the system; this needs to be centralised to reduce duplication and increase maintainability
  • If data retrieval is best handled in a central location
  • BasePageControllers can bloat with code not common to all implementers.
  • If BasePageController is refactored in different base controllers to remove unused abilities from inheritors, you can end up with a large deep inheritance hierarchy. This can lead to brittle designs and implementations and is hard to understand.
  • Page Controller breaks down when you need to control or coordinate a process across multiple pages.
This pattern works by channelling all calls through one Handler.

The Handler then delegates to one of a number of GoF Command objects. This Handler retrieves just enough information from the requested URL to decide which type of action to initiate and then delegates to a Command to carry out the action. The command does what it needs to and then chooses which view to use to render the page:

The Handler is usually implemented as a class rather than a Page, as are the Commands. The commands also should have no knowledge of HTTP although handed HTTP-derived data by the Handler. The Handler can decide on the Command to use (despatch) in a number of ways:
  • Statically: using conditional logic on the URL
  • Dynamically: putting the name of the Command in the URL
  • Dynamically: using a config file keyed on all or part of the URL to map to a command class name
Static decision making has the advantage of explicit logic, compile-time type checking, and flexibility in URL's. The Dynamic methods allow addition of commands and pages without changing the Handler.
The Handler can also be Decorated by what are known as Intercepting Filters to add behaviour to the Handler. Examples would deal with things like authentication, logging, locale. As a decorator, these Intercepting Filers can be applied multiple times creating a Filter Chain which could be loaded dynamically.
There is a variation which splits the Handler into two: one which received the Get or Post and interacts with HTTP to strip out the data (the ExamineUrl method in the sequence diagram above). It then hands that to a �despatching' Handler which knows nothing about HTTP, but makes the decision on which command to use. The advantage of this is that the coupling to HTTP is reduced making automated testing easier.

Pros and Cons of Front Controller

Pros:
  • The single Handler is in the perfect place to apply system-wide policies with no duplication
  • Since each Command is instantiated as a new object, Thread Safety is not an issue. (Note that it is an issue with the Model.)
  • Using Dynamic despatching is very flexible
Cons:
  • The Handler can become a performance bottle-neck. If it has to make a database query or parse say a large complex XML document to decide which command to despatch performance could be affected.
  • Front Controller is more complex than Page Controller. MSDN has an example implementation of in which it says: "Because Page Controller is built into ASP.NET, the additional effort required to implement Front Controller rather than Page Controller is very large. In fact, you must build the whole framework for Front Controller. You should do so only if your application warrants that amount of complexity. Otherwise, review Page Controller to determine whether it is sufficient."
  • The is no way inherent in the pattern to send the data collected by the ConcreteCommand into the View. One could always stuff a DataSet into the session object, but this re-introduces a dependency on the HTTP context into the Command. Other schemes are imaginable, though.
In other words, it's neat and it's powerful but make sure you really need it before sailing off into the complexities!

Page Controller Implementation

In this implementation, taken from MSDN, we have a BasePage web form with a blank form containing the following code in addition to the generated stuff:
	protected Label eMail;
	protected Label siteName;

	virtual protected void PageLoadEvent(object sender, 
							System.EventArgs e) {
	}
   
	protected void Page_Load(object sender, System.EventArgs e) {
		if(!IsPostBack) {
			string name = Context.User.Identity.Name;

			eMail.Text = DatabaseGateway.RetrieveAddress(name);
			siteName.Text = "PageControlled-site";
				PageLoadEvent(sender, e);
		}
	}
		
This form is the Base Controller for other pages, and puts the email address of the validated user in the header of each on the implementing forms. The email address is retrieved from the DatabaseGateway class, which stands for the Model in this example. Each implementing form has the following line:
		<!-- #include virtual="BasePage.inc" -->
		
This include file contains:
<table width="100%" cellspacing="0" cellpadding="0" ID="Table1">
	<tr>
		<td align="right" bgcolor="#9c0001" cellspacing="0" 
					cellpadding="0" width="100%" height="20">
			<font size="2" color="#ffffff">Welcome:
				<asp:Label id="eMail" runat="server">username</asp:Label>
			</font>
		</td>
	</tr>
	<tr>
		<td align="right" width="100%" bgcolor="#d3c9c7" height="70">
			<font size="6" color="#ffffff">
				<asp:Label id="siteName" 
						Runat="server">Micro-site Banner</asp:Label>
			</font>
		</td>
	</tr>
</table>
		
This sets up the header of the page and includes two ASP.NET labels - "eMail" for the email address and "siteName" for the site name. These are filled in by the Page_Load event in the BasePage. After filling them in, BasePage calls PageLoadEvent, a virtual function which will be overridden in implementing Page Controller pages, such as:
namespace ghytred.PageController {

	public class Page1 : BasePage {
		protected System.Web.UI.WebControls.Label pageNumber;
	

		#region Web Form Designer generated code
		#endregion

		protected override void PageLoadEvent(object sender, 
						System.EventArgs e) {
			pageNumber.Text = "1";
		}
	}
}
This simple page has a label which it fills in with a page number, but could have far more. This is an unrealistically simple case for Page Controller and BaseController but should get the idea across.

Front Controller Implementation

In keeping with the realism of the Page Controller example above, let's say that we now want to change the site name and get the email address from different databases depending on the URL. In the Page Controller example we can change the BasePage class as follows:
	protected void Page_Load(object sender, System.EventArgs e) {
		if(!IsPostBack) {
			string site = Request["site"];
			if (site != null && site == "macro") {
				LoadMacroHeader();
			} else {
				LoadMicroHeader();
			}
		PageLoadEvent(sender, e);
		}
	}
		
The LoadxxxHeader methods get the address and set the site name appropriately and we're done. However the base class now contains conditional logic; in this case it's probably OK, but in more complex cases it can become problematic quite quickly. Hence we'll do this with Front Controller.
For Front Controller, we need a Handler:
using System;
using System.Web;

namespace ghytred.FrontController {
	public class Handler : IHttpHandler {
		public Handler() {
		}
		public void ProcessRequest(HttpContext context) {
			ICommand command = 
			CommandFactory.Command(context.Request.Params);

			command.Execute(context);
		}
		public bool IsReusable {
			get { return true; }
		}
	}
}
		
We need to have a Command (we're using an interface - ICommand - in this example; there is no useful inherited behaviour). We also use a factory to return the appropriate command:
using System;
using System.Web;
using System.Collections.Specialized;

namespace ghytred.FrontController {

	public interface ICommand {
		void Execute(HttpContext context);
	}

	public class CommandFactory {
		private CommandFactory() {
		}
		public static ICommand Command(NameValueCollection parameters) {
			string siteName = parameters["site"];
			switch (siteName) {
			case "Macro":
				return new MacroCommand();
			case "Micro":
				return new MicroCommand();
			default:
				return new UnknownCommand();
			}
		}
	}
}
		
Now we configure the site to use the Handler by adding the following:
	<httpHandlers>
		<add verb="*" path="Page*.aspx" 
			type="ghytred.FrontController.FCHandler,FrontController" />
	</httpHandlers>
		
in the system.web section of the Web.Config.
OK, this is all set up to call our handler (class FrontController in assembly ghytred.FrontController.FCHandler.dll) on every access (verb="*") of URL's pointing to pages with names of the form Page*.aspx. The Handler is set up to get the appropriate Command object, and tell the command to execute the request. What are the commands like?
Each command is going to get the user's email address and the site name, and store them in the HttpContext. They are then going to redirect the request to a page that implements the View required.
Each command object will inherit from an abstract RedirectingCommand:
public abstract class RedirectingCommand : ICommand {
	
	protected abstract void OnExecute(HttpContext context);
	
	public void Execute(HttpContext context) {
		OnExecute(context);
		string url = String.Format("{0}?{1}",
					UrlMap.Instance[context.Request.Url.AbsolutePath],
					context.Request.Url.Query);
		context.Server.Transfer(url);
	}										  
}
		
This abstract class implements the Execute method and provides a hook for its inheritors - OnExecute - to slip in their own stuff. It calls this hook and then uses a utility class (UrlMap) to translate the Url requested to the actual page which implements the view. Finally, it transfers to that page.
The Commands look like:
public class MacroCommand : RedirectingCommand {
	protected override void OnExecute(HttpContext context) {
		string name = context.User.Identity.Name;
		context.Items["address"] = 
				MacroUsers.retrieveAddress(name);
		context.Items["site"] = "Macro-site";
	}
}
public class MicroCommand : RedirectingCommand {
	protected override void OnExecute(HttpContext context) {
		string name = context.User.Identity.Name;
		context.Items["address"] = 
				MicroUsers.retrieveAddress(name);
		context.Items["site"] = "Micro-site";
	}
}
public class UnknownCommand : RedirectingCommand {
	protected override void OnExecute(HttpContext context) {
		string name = context.User.Identity.Name;
		context.Items["address"] = 
"user " + name + " is unknown";
		context.Items["site"] = "Unknown";
	}
}
		
UrlMap would most likely work off a configuration file, but this example is hard-coded:
public class UrlMap {
	private UrlMap() {
	}
	// Show off Singleton implementation
	private static UrlMap instance;
	private static object thingToLock = new object();
	public static UrlMap Instance {
		get {
			if (instance == null) {
				lock(thingToLock) {
					if (instance == null) {
						instance = new UrlMap();
					}
				}
			}
			return instance;
		}
	}
	public string this[string requestedPage] {
		get {
			// prefix page name with "Actual"
			string[] parts = requestedPage.Split('/');
			int i = parts.Length - 1;
			if (i < 0) {
				i = 0;
			}
			parts[i] = "Actual" + parts[i];
			string actualPage = String.Join("/", parts);
			return actualPage;
		}
	}
}
		
So now our Handler catches all requests for Page*.aspx pages; gets hold of an ICommand object which does some work and translates the request to a page which creates the view required and transfers the call on to the view.
We will have the same structure as with the Page Controller - a BasePage plus two implementations. The BasePage class has changed though. Now it simply retrieves the data the Commands stored in the Context and puts them in the Labels; it's Page_Load looks like this:
	protected void Page_Load(object sender, System.EventArgs e) {
		if(!IsPostBack) {
			eMail.Text = (string)Context.Items["address"];
			siteName.Text = (string)Context.Items["site"];
			PageLoadEvent(sender, e);
		}
	}
		
The actual views will be called ActualPagexxx.aspx. They are exactly the same as the Page1.aspx in the Page Controller example - save that they should be called ActualPage1.aspx etc.

Notes on the implementations

If the page to be shown has to show data from a database, rather then a hard-coded number, then in Page Controller the work to get the data would have to be done in the View (Page1.aspx). While the BasePage can set something in the Context, say, to indicate which database should be used (Micro or Macro) the page itself has to test that and then do the work - unless it is abstracted out into the BasePage along with the email lookup. However, now the base page contains code applicable only to this particular query; perhaps we should have a different base page for other views which want different data. Now we're starting to build up a hierarchy of bases and the system is getting complex. Still, if all we want is to display a small amount of data from varying sources - or use a different CSS for instance - then it looks promising.
The Front Controller looks promising for cases where there is more work to be done than in this example. True there is a load of extra complexity is setting it up, but once set up it's easy to add more commands without changing the Handler. Also the Handler can be decorated with Filters to add new functionality; this would require adding a HandlerFactory (implementing IHttpHandlerFactory) which adds the decorators and specifying that factory in the Web.Config instead of the Handler, but that's not difficult.
So Front Controller is more flexible and allows simpler Views (in real cases, not the noddy bit of fluff above) than Page Controller. Front Controller is also Open for Extension and Closed to Modification. This is an important principle of OO design and Front wins here hands down over Page Controller. It's open to extension because all that is required to add extra functionality is to add more commands or more Handler decorators; you don't have to modify existing code to do that as you would in Page Controller.
On the other hand, Front Controller could lead to performance problems, and this must be watched for. Also, as MSDN says:

Cruel and unusual punishment. This implementation is a lot more complicated than Page Controller. This implementation does provide more options, but at the cost of complexity and a lot of classes. You must weigh whether or not it is worth it. After you have taken the leap and built the framework, it is easy to add new commands and views. However, due to the implementation of Page Controller in ASP.NET, you would not expect to see as many implementations of Front Controller as you would in other platforms.

I think I'll leave it at that.

Return to top