Tags

, , ,

Looking around the internet there are several implementation philosophies for the MVP pattern. There are also many implementations and I chose to build on the Atomic Object guys’ work: the Presenter First. My first implementation was in C# and I cover the basics in this post. Last year I applied this technique to a medium size C# project that was temporarily shelved. Maybe 80% of the functionality was there but I was diverted to do some work on a legacy application written in VB6. As it happens, part of that work involved new features with new screens and that is why I embarked in the VB6 implementation of the Model, View, and Presenter. In parallel with this project I developed an add-in for Excel 2007, also in VB6, and since it was mostly UI work, it also got the MVP treatment. This project has many user stories and did push the envelope of my implementation. When I went back to the shelved C# project I was in for a surprise, the code in the views and models was hard to understand. This was surprising because I had visited the VB6 code several times and hadn’t had the same problem. As it happens, the cause was an implementation detail. Several methods in different interfaces had the same signature and since I was not implementing the interfaces explicitly, VS.NET would not create duplicate methods. Of course, it was all my fault and I learned my lesson which is always implement your interfaces explicitly in the View and the Model and it goes to show that taking shortcuts when using this pattern has a high price. The issue did not arise in the VB6 implementation because all interfaces are implemented explicitly and it is not possible to share implementations. It was a funny instance where a limitation of the language forces you to write to better code.

Code duplication and side effects

One weakness of the MVP pattern is that it creates a tension between feature isolation and code duplication. In an ideal scenario, and ignoring any back-end activity, each user story would travel from the View to the Model, across the Presenter, one way only. Then, in another isolated process, the Model reactions, if any, would propagate through the Presenter and change the View. If all user stories are implemented in this fashion you have 100% isolation between features but you also have code duplication. On the other hand, if you share some of the implementation like I did, you may end up with confusing code and, worst of all, side effects. These side effects are the killer of any MVP implementation and lead to unexpected bugs. They are caused by sharing code and models amongst stories. Going back to my ideal scenario, I should add that each user story must have its own Presenter and Model. The View is obviously shared but the other elements of the triad should never be shared. When you start a new feature and implement the first few user stories it is very tempting to reuse the Model. This simplifies state management and reduces the number of files in the project and if you are implementing a very simple dialog then you will be OK. If you are developing the main screen of an application or a complex dialog then you will get hurt very soon. The problem is that you will end up relying on the order each story is run. Some of these interdependencies are inevitable and logic as you wouldn’t want to save a file before you open it, but other more subtle dependencies, like one story relying on another story setting a variable, will come back to haunt you in the form of bugs. These issues are easy to miss because we tend to run our unit tests in a fixed order and are only caught when the user clicks his way around unpredictably.

One possible solution to this tension is pushing operations common to several user stories into interfaces implemented by the View. Since all presenters have a reference to the View they can always cast it to one of these interfaces. I have made some inroads in this direction applying it to UI state. If any Presenter needs to change the UI into busy mode then it calls into this interface. The implementation of the interface can be very simple, setting a few enable properties, or it can be more involved using data binding to drive complex UIs. This strategy doesn’t apply to the models since they are ephemeral. Here I minimize the duplication through the creation of helper classes but the solution is not as neat and effective as the previous one.

State Management

A big decision with MVP is where you save the state. If we use different models then it cannot be kept there because they are transient just like all the presenters are too. The only entity common to all MVP triads is the View. For the cases when the state is a file name or an identifier and it is easy and cheap to load then I save the state in the View. I create an interface (IQueryState is a good name) which is implemented by the View and queried by the presenters which then push that state to their models. This interface is very simple and only has two methods: SetState and GetState. If the state is an in-memory object that is expensive to load and there may be several of them (as in several documents open in the application) then I save it in a global registry against a cookie that is then held in the View and also queried through IQueryState.

Conclusion

It is a very good idea to implement the MVP pattern in your UIs but it is neither simple nor free of pitfalls. I hope I managed to explain a couple of them here and I will add more when time allows.

Advertisements