Friday, February 1, 2008

The Model, View, and (Virtual) Presenter Pattern



One of the greatest weaknesses of the
MVP/MVC pattern is that although the the model is completely separated from the view (and vice-versa), the view in the MVP pattern has to have a concrete dependency on its corresponding Presenter in order to function. At first, there doesn't seem to be a way to completely isolate all three components of the MVP pattern from each other, given that the view has to have a reference to a concrete presenter. The presenter, on the other hand, knows nothing about the concrete view, other than the IView interface that is implemented by the view itself; it's a one way dependency, and yet, something just doesn't 'feel' right here...

The Epiphany


...and then that's when the questions started to hit me:
Does the presenter even have to be a concrete presenter at all? Is it possible to replace a concrete presenter with an interface type, and what would that interface look like? Are all presenters the same, or do they just all represent some sort of abstract concept that can't be centralized into a single common class?

My proposal for changing the MVP pattern is this: What if we convert the concept of the presenter from a concrete class to an abstract metaclass? If we change the presenter from a concrete class to an abstract implementation of a concept, we can effectively eliminate the dependency between all three parts of the MVP triad. It might sound complex, but the implementation is actually quite simple. In this variant of the MVP pattern, we can completely eliminate the view's dependency on a concrete presenter by converting the concrete presenter dependency into a collection of interfaces that the view requires in order to function.

A Quixotic Attempt


A naive approach would be to try to find a common interface to be implemented by presenters, but this poses a problem--no two presenters will ever be exactly alike. A presenter that will handle a list view won't always have the same interface as a presenter that handles a date field. In addition, there might be presenters which might hold some business or validation logic that other presenters might not necessarily share. In other words, we're dealing with a situation where each presenter interface allmust be heterogenous for each view type, and that makes it difficult to group all of the presenters into a single interface that could be used by each view type.

Dead End?

At first, it might seem like we have no choice but to make each presenter concrete and customize each one of them to fit their corresponding views--but what if we refactored each one of these concrete presenters into multiple interface dependencies for each view? If you think about it, a concrete presenter is nothing but a collection of different responsibilities assigned to a concrete interface. For example, a DatePresenter class might have a few methods that check if the date value presented in the view is valid. The same DatePresenter class also might have methods which can persist the date value to the database. The crux of the concrete presenter in the MVP pattern is that the date view relies on this same DatePresenter to control its behavior. There's a one-way separation between the view and the presenter, and there has to be a way to completely isolate the presenter from the view itself.

An Unnecessary Coupling

A view (for the most part) doesn't need a reference to a concrete presenter class as much as it needs a reference to the interfaces and responsibilities that each one of those interfaces represent. In fact, if I were to refactor a concrete presenter, extract each one of its interfaces, and have each one of those interfaces represent a responsibility that the target view requires, it almost seems like I've converted the concrete presenter into a de facto service container.

Each presenter responsibility corresponds to a service, and each service corresponds to an interface type. Since no two presenters are alike, one could even say that each presenter has a list of services that it offers to each view. So for me, the next logical question is this: If concrete presenters are nothing but de facto service containers, why not use a real service container (aka IoC container) and ditch the concrete dependency altogether?

If a concrete presenter is actually nothing but a degenerate IoC container in disguise, then I can take each one of the view types and replace their concrete presenter dependencies with a single dependency--a dependency on real IoC container by itself. What makes this interesting is that in this variant of the MVP pattern, the 'P' in the MVP pattern no longer exists as a concrete class. The view's concrete dependency on the presenter has been supplanted by interface dependencies supplied by an IoC container. Using this scheme, the view knows nothing about the actual presenter concrete class, and the presenter knows nothing about the concrete view class. As an added bonus, we can even separate the presenter from the concrete model by having the presenter rely on interfaces supplied by the model, and vice versa. The best part about all this is that (aside from the interface dependencies) all three parts of the MVP triad are completely isolated from one another. The only dependency that all three parts of the MV(P) pattern share is the IoC container itself, and since most IoC containers are easily configurable by design (namely LinFu), this dynamically gives us complete control over all the dependencies of a given application...

Note: So far, all of this is just theory, and I have to write it down somewhere before I forget it. I think this pattern can help quite a lot of people, and hopefully I can test it soon.

9 comments:

  1. This means putting alot more code into your view though which complicates testing. Also since your view likely derives from some type of Form class, this means you have lots of repetitive code which could otherwise exist in presenter base class.

    I use the passive view MVP variant where the model is simply an interface which both the view and presenter must implement. The presenter is the only 'active' component and it can only communicate with the view via the model interface.

    The presenters form a heirarchy which ultimately derive from a base presenter which is basically an IoC container with a few extra services and convenience methods.

    So this method also gives the ability to use multiple (inherited) presenters for each view, or multiple views such as windows forms and asp.net view from a single presenter or heirarchy of presenters.

    ReplyDelete
  2. Hmmm...well, I knew this would take more than just a passing explanation, so hold that thought for a moment, and I'll go cook an example up :)

    Suffice to say, the approach that I'm describing doesn't add any more code into your views than the current dependencies they have on an existing presenter. The most relevant part about this approach is that from the View's perspective, the Presenter is nothing but a logical collection of presenter interfaces, and the service container is responsible for putting it all together. The details of how the presenters are organized are, in effect, abstracted away by the service container...

    Anyway, this is really hard to explain without an example, so excuse me while I go off and make one up... :)

    ReplyDelete
  3. In my MVP pattern the presenter is the only active part. The view is as "humble" as possible and doesn't know the presenter. The view raises events and the presenter listens to them. The view never asks but is always asked by the presenter (through the IViewXYZ interface). The same is true for the Model. The model doesn't know any Presenter(s) but is always asked (for services) by the presenter and replies by raising events to which in turn the presenter listens. So in the end there is NO direct dependency between the 3 parts in the MVP triade. By the way: since I'm using Binsor (Windsor + BOO) to configure my IoC container every thing just "magically" happens

    ReplyDelete
  4. I'm definitely going to have to take a look at the BOO language then. It seems interesting..

    ReplyDelete
  5. yeah, BOO is great. By using Binsor (by Oren Eini!) I can completely abandon xml-type configuration (dislike the angle braket noise...) and even integrate logic into the config file.
    By the way: when can we expect to get our hands onto your AOP code?

    ReplyDelete
  6. @gabriel:

    You can take a look at LinFu.AOP here:

    http://www.codeproject.com/KB/cs/LinFuPart6.aspx

    Enjoy!

    ReplyDelete
  7. In the comments of my article about the Presentation Model on CodeProjec, people recommanded LinFu when I said I am looking for a light weight IoC container.
    http://www.codeproject.com/KB/smart/PMinAction.aspx

    I followed LinFu article to your blog and found your houghts on MVP which I agreed very much.

    How do you think about the Presentation Model pattern?

    ReplyDelete
  8. The Presentation Model looks a lot like a self-contained implementation of the MVC pattern implemented within the view itself. What makes it interesting, however, is that you can implement most of its functionality by using LinFu's universal event handling to synchronize the presentation model and the view. Simple.IOC alone should make wiring everything together amazingly easy.

    In my opinion, the presentation model looks like a different way to organize the MVC/MVP pattern so that the behavior and the state are fused together into one class. I can't say that I agree with this approach, given that the whole point of the MVC/MVP pattern is to separate all three parts from each other so that all of the parts would be easier to maintain. The fusion of the model and behavior might make the code a little too complicated, and unless there's some quantifiable gains to be made from shifting from MVC to the Presentation model, I think I'll stick with the "tried and true" way, which is MVC/MVP. :)

    ReplyDelete

Ratings by outbrain