There’s something nice about returning to an old design pattern. Comparing it with the pattern you’re currently using gives you new insights about both.
When I was writing ASP.NET WebForms apps, my team organized the UI layer with an MVP (Model View Presenter) pattern, preferring the Passive View variant. This allowed us to write Views that were small and lean, pulling as much logic as possible out of that untestable wasteland of UI markup and code-behind. More recently, I’ve been developing ASP.NET MVC applications (using the MVC (Model View Controller) pattern, naturally), and I like how my Views can be straightforward HTML with a few fill-in-the-blank spots for the data I want to display; much simpler to understand than the old WebForms server controls. But my most recent project, I wrote in WPF (Windows Presentation Foundation) due to some specific needs of the client, and I had to re-open the UI design pattern question. I decided on MVP, and this gave me a great opportunity to contrast the two patterns.
My goal in choosing any pattern is readable, understandable code. Even if I’m the only developer on the project, reading my own code, if six months have elapsed, it might as well have been written by someone else. Patterns inherently give structure, but more importantly, they are recognizable. Reading the code, you can spot it: “Oh! This is an MVP app. Okay, I’ll look for a Presenter class to be managing the interactions with the View. …There it is!” Future maintainers and enhancers (including your Future Self) will appreciate your consistency; patterns help you write “No Surprises” software. Both MVP and MVC meet this criterion; they are recognizable patterns, each with a distinctive fingerprint.
My secondary pattern-choosing criterion is testability: How well does this pattern let me cover every interesting part of my app with unit tests? While I see the value in integration tests and full-system tests, I’m strongly prejudiced against them. Integration tests are finicky, and full-system tests are slow. I’m unapologetically feisty about my unit tests. The better pattern is the one that not only makes it easy to test, but makes it hard to write untestable code. In other words, prefer the pattern that leads your team to fall into the “pit of success.” MVC beats MVP on this point. Let me explain.
Consider a continuum, from Untestable Scary Code to Clean, Verified Code. WebForms without the rigor of an MVP pattern, with heavy reliance on the code-behind, is far on the left of that continuum. A lot of business logic and application flow control can be hidden in the least testable layer. When you apply MVP to your WebForms app, the pattern guides you to write very slim code-behind files, with methods that are usually one line long, immediately deferring to the Presenter. This is closer to the middle of the continuum, where you can test your Presenters, provided you trust your Views to remain humble and hand over control promptly. MVC, as implemented by the ASP.NET MVC framework, gives you no code-behind files. There’s no temptation to put logic into the View because there’s hardly anywhere to put it. Control logic naturally fits in the Controller, which is a plain class you can instantiate and exercise in a unit test. The easiest path produces the cleanest result. Nice.
Why did I use MVP at all? It’s necessary in a framework that brings the View into existence first. Both WebForms and WPF start a conversation with the user by locating and instantiating a View. The View then constructs its Presenter and injects itself into it. MVC implementations start by instantiating a Controller. Unlike MVP Views, which have to talk to their Presenters, MVC Views can remain ignorant of their Controllers, and this allows them to stay logic-free and focused on rendering the UI. When using MVP, it takes vigilance to make sure your Views never do anything interesting, anything you’ll later wish you had covered with a unit test. And anything that takes vigilance is likely to drift over time.
It was fun to revisit my “old” way of structuring applications, because it made me think critically about the relative merits of MVP versus MVC. I had an instinctive feeling for which one I preferred, and comparing them helped me see explicitly that MVC increases my confidence in the correctness of my team’s code.