Model-View-Presenter implementation thoughts

design-patternsinterfacesui

I'm trying to get a good grasp of how to implement good decoupling between a UI and the model, but I'm having trouble figuring out exactly where to divide the lines.

I've been looking at Model-View-Presenter, but I'm not sure exactly how to go about implementing it. For example, my View has multiple dialogs..

  • Should there be a View class with instances of each of the dialogs? Then in that case, how should the dialogs interact with the Presenter? ie. if an individual dialog needs to request data from the Model via the Presenter, how should the dialog get a reference to the Presenter? Via a reference to the View given to it during construction?
  • I was thinking maybe the view should be a static class? Then the dialogs GetView and get the Presenter from there…
  • I'd been thinking about setting it up the Presenter with ownership of the View and Model (as opposed to the View having the Presenter and Presenter having Model) and the Presenter registering callbacks for events in the View, but that makes it seem a lot more coupled (or language depended, at least.)

I'm trying to:

  1. make this as decoupled as possible
  2. ideally make it possible to couple the Presenter/Model with Views of other languages (I've not done a ton of inter-language stuff, but I know it's possible, particularly the more void(void) I can stick to, at least a C# app with a C++ library…
  3. keep the code clean and simple

So.. any suggestions how the interactions should be handled?

Best Answer

Welcome to a slippery slope. You've by this point realized that there is an endless variation of all the model-view interactions. MVC, MVP(Taligent, Dolphin, Passive View), MVVM just to name a few.

The Model View Presenter pattern, like most architectural patterns is open to a lot of variety and experimentation. The one thing all the variations have in common is the role of the presenter as a "middleman" between the view and the model. The two most common are the Passive View and the Supervising Presenter/Controller - [Fowler]. Passive View treats the UI as a very shallow interface between the user and the presenter. It contains very little if any logic, delegating as much responsibility to a presenter. Supervising Presenter/Controller tries to take advantage of the data binding built into many UI frameworks. The UI handles data synchronization but presenter/controller steps in for more complex logic. In either case the model, view and presenter form a triad

There are many ways to do this. Its very common to see this handled by treating each dialog/form as a different view. Many times there's a 1:1 relationship between views and presenters. This isn't a hard, fast rule. Its quite common to have one presenter handle multiple related views or vice versa. It all depends on the complexity of the view and the complexity of the business logic.

As for how views and presenters obtain a reference to each other, this is sometimes called wiring. You have three choices:

View holds a reference to presenter
A form or dialog implements a view. The form has event handlers that delgate to a presenter using direct function calls:

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

Since the presenter doesn't have a reference to the view, the view has to send it data as arguments. The presenter can communicate back to the view by using events/callback functions which the view must listen for.

Presenter holds a reference to view
In the scenario the view exposes properties for the data it displays to the user. The presenter listens for events and manipulates the properties on the view:

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

Both hold a reference to each other forming a circular dependency
This scenario is actually easier to work with than the others. The view responds to events by calling methods in the presenter. The presenter read/modifies data from the view through exposed properties.

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

There are other issues to be considered with the MVP patterns. Creation order, object lifetime, where the wiring takes place, communication among MVP triads but this answer has grown long enough already.