As soon as I wrote this title I felt the compulsion to explain why would anyone spend time implementing a bleeding edge pattern on a dying a language and framework. And there are two answers: because Visual Basic 6 will have a long death and to alleviate the burden we all carry with its death.
MVP is most possibly not new in VB6 and I am sure that many good developers out there have used it and its close cousin MVC (Model View Controller) in many applications. Unfortunately I never got the chance to see or work on such applications. Nine out of ten times the VB applications created in the enterprise are either a mass of property setting and event handling code or, in the best cases, timid inroads into interface based programming. Having worked in c# and vb.net using the latest ideas like Presenter First and Dependency Injection I felt that we lost something back in the day and came back to redeem VB6’s bad fame. And I plan to do this by proving that it is possible to create clear and testable applications using VB6’s best weapon: interfaces.
An MVP is composed of three entities: The Model, the Presenter and the View. Usually each maps to a class which communicate with each other using interfaces and events. The interpretation of MVP that I am following here is called Presenter First and both are introduced here: http://en.wikipedia.org/wiki/Presenter_First
The main objective of an MVP implementation is to isolate the View from the Model using the Presenter as the coordinator of the interaction between them. The Presenter First (PF) approach adds another objective which is to reduce the View’s code to a minimum to minimize its test requirements. PF also implies creating user stories and direct the application design from them. This leads to a very agile approach to UI coding where you start by creating a presenter class for a “when”, like “When the application loads” or “When the user presses save”, and then add the interactions between the UI and the Model to the Presenter. Using c# this could be exemplified this way:
void _view_ApplicationLoaded(object sender, EventArgs e)
When the application loads then the View raises an event that is caught in the presenter which then instructs the Model to load the application data. As you can see this is a single direction instruction where the View code could be as simple as:
private void Form1_Load(object sender, EventArgs e)
ApplicationLoaded(this, new EventArgs());
On the other end of the chain the Model loads the data from a data source and raises itself an event also caught by the Presenter like this:
void _model_DataChanged(object sender, EventArgs e)
Once again it is up to the Presenter to tell the other end what to do, in this case show a list of names. You can see the formal relationships in the figure below where the dotted line denotes events and the filled line commands or direct relationships.
But, this is supposed to be about VB6 not c#! Well the c# is here because I tried it first in c#. It is also true that .NET lends itself very well to MVP and PF because of its clean implementation of interfaces and delegates.
And now in VB6
My first implementation of the MVP/PF in VB6 mirrored the .NET implementation where each user story maps to a triplet and the View and the Model communicate with the Presenter through events. For the user story “When the application loads” the relationships for this design would be:
The downfall of this first design was that in VB6 you can define events in interfaces but they don’t really serve any purpose. You are not forced to implement them and there is no way to raise them too. The first workaround I used was defining the events in the View and in the Model and declare concrete implementations of the View and Model WithEvents in the Presenter. This worked well for a while but soon I was lost in a maze of unidentifiable events which I kept forgetting to raise. It was clear that this implementation did not scale at all so I scrapped it.
As in everything COM the solution was in using interfaces. COM events use interfaces so I replaced the standard events with custom interfaces which are implemented by the Presenter and used in the View and in the Model. This resulted in the following relationships:
Our MVP is still true to its origins but the Presenter now implements two interfaces: one representing the View’s events and another one representing the Model’s events. This makes sense as events can be thought in terms of an inverted interface implemented by the listener and manipulated by the subscriber. For the same story as above, “When the application loads”, the relationships are now:
Wow, you may say, that really is complexity. In fact it is not that bad, there is a compromise which is that we must provide the plumbing ourselves and the overhead in terms of files is sizeable. To minimize the excise I created a simple VB6 add-inn that takes care of adding all the files ready with most of the boilerplate code. Another decision I made was to adopt a naming convention to simplify file management.Presenter class: <StoryName>Presenter, e.g. AppLoadPresenter
View interface: < StoryName >ViewInter, e.g. AppLoadViewInter
View events interface: < StoryName>ViewEvents, e.g. AppLoadViewEvents
Model interface: < StoryName >ModelInter, e.g. AppLoadModelInter
Model events interface: < StoryName>ModelEvents, e.g. AppLoadModelEvents
Model: <StoryName>Model, e.g. AppLoadModel
Its biggest sin is not starting all the interfaces with a capital I and the reason I do this is to keep all the classes of the same user story together. I adopted this convention because it was proving quite hard to find things in VB6’s IDE after as little as three user stories were in place.
All the same you may think that there is too much fluff in this design. I think it is worth the hassle, first because most of it is generated by an add in, second all the application logic is in the Presenter and the Model which are unit test friendly and finally you get, for lack of a better name, “Strongly Typed Code”. I call it Strongly Typed Code because the structure literally forces you to follow a set path for each user interaction or story. This creates self documenting code that does not compile until you finish all the required pieces.
Our design is finished and over the next instalments I will cover each part of the design as well as the add-in and some code examples.