In this post I will talk more about the "MVP" pattern and why I don't think you should focus on building the IViews and Presenters to reuse them for other UIs.
Some people try to create an IView (When I use the IView in this post, I’m talking all the view’s interface that a presenter will work against) and a presenter/controller (I will use the word presenter instead of a controller) that should be reusable for any kind of UI, for example Windows App and a Web based. My suggestion is not to focus too much on a common implementation of the IView and the presenter to make them reusable by other UIs when you create them, instead take advantage of the platform capability but use the Supervising Controller pattern to make it possible to test the complex logic. I will in this post try to explain why I don’t try to create IView and presenter that should be reusable between different kind of UI (When I’m talking about different kind of UI, I talk about Windows Forms and Web Form etc), and why I don’t think you should focus on a generic implementation. In some situations you can use some valuable patterns to solve some specific problems, that other UIs’ can’t reuse, or they can but it require extra solutions that can be complex to create. If you need to reuse some complex logic that is for example common for both a Web form and a Win form, make sure to refactoring them out from the presenter into a common classes and reuse that logic in different presenters, instead of putting it into one presenter that you will try to reuse among several UIs.
First of all let’s take a look at the Supervising Controller. The main purpose of the pattern is for testability issues. We separate logic out from the view into the presenters/controllers. By doing this we can create unit tests that will test the logic. I think everyone agree on this. So by looking of the implementation of the Supervising Controller, we can see that we can sort of reuse the presenter and the IView for other UI as long as create a view that uses or IView interface.
Here is what Martin Fowler writes about the Supervising Controller:
“The essence of a good Supervising Controller is to do as little as possible. Let the view handle as much as possible and only step in when there's more complex logic involved.”
As long as we can rely on simple views in our different UIs, we can sort of reuse the IVIews and the presenters/controllers. But we also need to have very good knowledge about the different UIs, and can also identify the common denominator of the different UIs. The common denominator can decrease when the abstractions of the UIs increases (I will talk more about later). If we only focus on the complex logic and add it to the presenter and also use what the UI can offer us, we can reduce the complexity in some situation and make it easier to reuse the presenter and IView. But still we can face several problems. If we open up a child window, which should synchronize the data from the child window into the root window, how should we handle that in a generic way so it will work for all UIs? Or if we have several windows open and one changes in one window should reflect all the others to refresh them self.
When we are working with the Supervising Controller we simply don’t want to put a dependency between the different views nor the presenters/controllers. The reason is that each presenter should only know about one view (which it is a presenter for); a view should only be bind to one presenter. If we now have an app with a root view and a child view, for example a root view opens a child view which should give the root view some data back. Let’s assume we want to reuse the child view for other views, in that case we don’t want the two views to have a dependency to each other (We don’t want the child to know about the root, we don’t want the root to know about the child). There are two patterns that can be used to transfer data from one view to another, the Flow Synchronization and the Observer Synchronization. We probably don’t want to use the Flow Synchronization pattern for this, the reason is that Flow Synchronization is a pattern for make sure a view will update itself when other views state is changed and the changes should be shared across views in the applications. This will sort of couple every view to another view in an application, and changes in one view (implementation changes) can lead to several changes in other views because of the coupling, this can be messy and hard to maintain. Another solution for sharing data between views is by using the Observer Synchronization pattern. In that case we use events on the model object that we can subscribe to. So if the state of the model object is changed, we can make sure each view that uses that model object can update itself. The Observer Synchronization pattern will remove the coupling between views. If we use the Observer Synchronization patterns together with the Supervising Controller, the Observer should be in the presenter/controller. By using this pattern the model object need to trigger an event when the state of the object is changed, by doing so, the presenters/controllers that need to know when the changes take place, can simply subscribe to the model object’s event. For example:
public delegate void DomainChangeHandler(object sender, EventArgs e);
public class Customer
{
public event DomainChangeHandler StateIsChanged;
...
public string FirstName
{
set
{
...
StateIsChanged();
}
}
}
Note: Because each domain object would probably use the StateIsChanged event, we could use the Layer Supertype to put the event into the Supertype (base class).
The presenter can now hook up to the Customer’s SateIsChanged event and for example perform a refresh of the view:
public class CustomerViewPresenter
{
...
private Customer _customer;
public CustomerViewPresenter(Customer customer)
{
this._customer += new DomainChangeHandler(Customer_Changed);
}
public void Customer_Changed(object source, EventArgs e)
{
//refresh the view
}
}
Note: The Customer is passed as an argument to the constructor to the Presenter, it’s because we need to have the Customer in the applications state, so each view that want to display the customer, uses the same instance. We could have created some kind of an Identity Map, where we could get the customer from; in that case we don’t need to pass the Customer into the presenter. But I will make the code easy to read and make it small in this post.
Now if any other presenter would change the customer, the Customer_Changed method of the CustomerViewPresenter would be called and make sure the presenter update the view with the new information. As you can see, this will make it difficult to use in a Web based app, where the protocol we use is a request/response. It works great in Window Form applications because it’s not stateless like a web app.
As you can see the presenter that is created above will not work very well in a Web based application (remember a Web From is disconnected from the server after the page is processed and we got the response). As you may know, a server-side event will not notify the client, so in this case the presenters can’t tell the different web forms to update them self . It can sort of be solved, but it’s not easy all time and can be difficult to maintain. For example we can use a poller from the views that will ask the server for any changes in the model objects. The problem with this solution is that we need to have all the opened web forms’ presenters in the state on the server if we use the Observer Synchronization and want to know when a model object state is changed. One problem we will face here is the way to find a solution to release the presenter from the sate and also the subscription of the events when the web from is closed on the client, if not, we can have memory leaks. This can for example be done by have some kind of a time window, so if the poller hasn’t asked for any changes after a specified time, the presenter will be released. But is it worth the time and effort to even try doing something that will make the Web From behave like the Win form in this case? If we are going to build more AJAX enabled web sites, we will put more logic to the client-side, if we will use Silverlight, we also add more logic on the client-side, for example when a button is pressed on the client-side we can do an asynchronous call to a service to get data that should fill the view with information. So in this case we can’t reuse the same presenter and IVIew we created for the Windows application, we need to have two different implementations of the presenter and IView. AJAX and Silverlight abstracts the complexity of writing rich web based clients, and with this new abstraction we need to move the view and presenter up to the client-side.
Martin Fowler writes the following about the Observer Synchronization pattern:
“Web user interfaces are effectively a sequence of screens, this is an example where the protocol of the client/server connection makes Observer Synchronization very difficult to do.”
Let’s assume we have a Windows form with checkboxes, each checkbox represents a color. When a user selects one checkbox we will trigger an event that will automatically set a background color of the window to the selected color, here is an example:
Form:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
this._presenter.SetBackGroundColor(Color.blue);
}
Presenter:
public SetBackGroundColor(Color color)
{
this._view.BackGroundColor = color;
}
IView:
Public interface IMyView
{
Color BackGroundColor {get; set;}
}
View:
public class MyView : IMyView
{
public Color BackGroundColor
{
get { return this.BackColor; }
set { this.BackColor = value; }
}
}
Note: Don’t hang up to this example; I only use it for demonstration purpose and use the simplest scenario that popped in my mind.
As you can see, we can theoretically easy reuse both the IMyView and the Presenter for another UI, for example a Web form. The problem here is that we need to make a postback when we select a checkbox only to set the background color of the window. So is it worth another roundtrip to the server, it will affect the performance of our app. Most developers would probably not want the extra roundtrip, so instead they use a client-side script that will do the work (set the color dynamically). As you may know, AJAX is a solution that almost every web developer is using to increase the user experience and create rich clients. So in this case the reuse will only make the app clumsier. But back again to what Martin Fowler wrote:
“The essence of a good Supervising Controller is to do as little as possible. Let the view handle as much as possible and only step in when there's more complex logic involved. One of the prime reasons to use Supervising Controller is for testability. Assuming the view is hard to test, by moving any complex logic into the controller, we put the logic in a place that's easier to test.”
So in this case, the logic is not complex, it easy logic. So in the Windows form app, we can still use events because it’s the common way when we build Win Forms, but in a web based app, we will probably use client-side script, and try to avoid the extra run trip to the server. There are several other similar issues where we don’t want to do a roundtrip to affect the performance or the user experience only to reuse an IView and a presenter.
We should use what the UI can offer us (“let the view handle as much as possible”), and put complex logic that we need to test into the presenter (“by moving any complex logic into the controller, we put the logic in a place that's easier to test.”). This can of course left out some code for automation testing. But I think it’s more important to create UI that is easy to use and also increase user experience, than testing everything with unit tests. The important thing is that the tests cover the complex logic, which will be located in the presenter. In this case other tests during the development will cover the UI specific code.
Conclusion
Have this in mind to build a Windows application is not the same as building a stateless and request/respond application. To make it possible we need to find the common denominator between the UIs, which can reduce the capability to a user friendly application and set limit to how we can create our UI. We can for some UIs create an IView and a presenter that will work for the different UIs, but we will probably spend time on new solutions to make it possible to reuse (For example if we use the Observation Synchronization pattern, which is easy to use in a Windows application but not in a Web based application, to make it work and to refresh the views after the state has been changed, we need create a new solution). If we try to build a IView and presenter that should be used for both Windows app and Web Form, we must also have in mind that more and more want the web application to be richer and more user friendly. We have new way of building rich web apps by using AJAX and Silverligth, both work on the client-side, and we can for example do asynchronous call to a service over the network to get the data which should fill the view with information. So in this case we need to put an IView and presenter on the client-side. So my last advice is to not focus on creating a reusable IView and presenter for different platforms, instead create it for the current targeting platform. If anyone doesn’t agree with me, I’m really interested in hearing about your thoughts etc, and you are all welcome to try to persuade me about that I have wrong. But it will probably not be easy, but I will let the door be open ;)