39

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?

2
  • Have you looked at this article?: en.wikipedia.org/wiki/Model-view-presenter
    – Bernard
    Commented Mar 22, 2011 at 16:05
  • 2
    I have.. I found it a bit quick and high level, I'm looking to understand better how to handle multiple dialogs in a large project with as little coupling as possible..
    – trycatch
    Commented Mar 22, 2011 at 16:25

4 Answers 4

45

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 (MVP) 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.

1
  • 2
    This is definitely helpful. Communication between the triads and lifetime is where I'm currently having trouble now that I'm grasping some of this.
    – trycatch
    Commented Mar 23, 2011 at 13:31
10

As everyone has said, there are dozens of opinions and not any single one of them is right or wrong. Without getting into the myriad of patterns and only focusing on MVP here's some suggestions on implementation.

Keep them separated. The view should implement an interface which forms the bond between the view and presenter. The view creates a presenter and injects itself into the presenter and exposes the methods it offers up for the presenter to interact with the view. The view is responsible for implementing these methods or properties any way it wants. Generally you have one view : one presenter but in some cases you can have many views : one presenter (web, wpf, etc.). The key here is that the presenter knows nothing of UI implementations and only interacts with the view through the interface.

Here's an example. First we have a view class with a simple method to display a message to the user:

interface IView
{
  public void InformUser(string message);
}

Now here's the presenter. Note that the presenter takes in a IView into its constructor.

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

Now here's the actual user interface. This could be a window, a dialog, a web page, etc. Doesn't matter. Note the constructor for the view will create the presenter by injecting itself into it.

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

The presenter doesn't care about how the view implements the method it just does. For all the presenter knows, it could be writing to a log file and not even showing it to the user.

In any case, the presenter does some work with the model on the back end and at some point wants to inform the user about what's going on. So now we have a method somewhere in the presenter that calls out to the views InformUser message.

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

This is where you get your decoupling. The presenter only holds a reference to an implementation of IView and doesn't really care how it's implemented.

This is also a poor mans implementation as you do have a reference to the Presenter in the view and objects are set via constructors. In a more robust solution you would probably want to look at inversion of control (IoC) containers like Windsor, Ninject, etc. that would resolve the implementation of the IView for you at runtime on-demand and thus making it even more decoupled.

6

I think it's important to remember that the Controller/Presenter is where the action really takes place. Coupling in the Controller is inevitable because of necessity.

The core point of the Controller is so that if you make a change to the View, then Model doesn't have to change and vice versa (if the Model changes the View doesn't have to either) because the Controller is what translates the Model into the View and back again. But the Controller will change when either Model or the View changes do because you effectively have to translate within the Controller how the Model is to Viewed how to get changes made in the View back into the Mode.

The best example I can give is that when I write an MVC app, I am able to not only have data in the GUI view, but I can also write a routine that pushes data pulled from the Model into a string to be shown in the debugger (and by extension into a plain text file). If I can take Model data and translate it freely into text without changing the View or the Model and only the Controller, then I am on the right path.

That being said, you will have to have references between the different components to make it all work. The Controller needs to know about the View to push data, the View needs to know about the Controller to tell it when a change has been made (like when the User clicks "Save" or "New..."). The Controller needs to know about the Model to pull the data, but I would argue that the Model shouldn't know about anything else.

Caveat: I come from a totally Mac, Objective-C, Cocoa background which really pushes you into the MVC paradigm whether you want to or not.

5
  • This is definitely my goal. My main problem is how to set up the View - whether it should be a class with an instance of each dialog, and then use View.Getters which call Dialog.Getters, or if the Presenter should be able to call Dialog.Getters directly (this seems too tightly coupled, so probably not?)
    – trycatch
    Commented Mar 22, 2011 at 16:10
  • I think the Presenter/Controller should be fully responsible for the Views, so the latter. Again, some coupling is bound to happen, but at least if the direction of responsibility is clear, then maintenance should be easier in the long run. Commented Mar 22, 2011 at 16:13
  • 2
    I certainly agree the P/C should be responsible for the View, but I thought part what was supposed to make MVP powerful was the ability to pull the entire UI library out and plug a new one in and with some massaging (dllimporting and whatnot) be able to run another one in it's place. Wouldn't this be more difficult with the Controller/Presenter accessing the dialogs directly? I'm certainly not trying to argue, just further understand :)
    – trycatch
    Commented Mar 22, 2011 at 16:22
  • I think the real power comes from two directions: The first being that the View and the Model don't have anything to do with other, and the second that the majority of development work, the app's engine, is done in a neatly-contained unit, the Controller. But some bleeding of responsibility is bound to happen. At least the majority of swapping of interfaces will be done in the Controller and any linking from the View would be minimum. As others have said, some bleeding of logic is expected and allowed. MVC isn't a magic bullet. Commented Mar 22, 2011 at 18:14
  • The important point for decoupling is that presenter ONLY accesses view through well defined interfaces (UI library independent), so that is how UI library can be replaced for another one (another one that will implement the same interface for form/window/dialog/page/control/whatever) Commented Feb 27, 2012 at 16:03
3

In general, you want your model to encapsulate all the interactions with that model. For example, your CRUD actions (Create, Read, Update, Delete) are all part of the model. The same goes for special calculations. There's a couple good reasons for this:

  • It's easier to automate your testing for this code
  • It keeps all that important stuff in one place

In your controller (MVC app), all you are doing is collecting the models you need to use in your view, and calling the appropriate functions on the model. Any changes to the model's state happens in this layer.

Your View simply displays the models that you prepared. Essentially, the view only reads the model and adjusts its output accordingly.

Mapping the general principle to actual classes

Remember that your dialogs are views. If you already have a dialog class, there is no reason to create another "View" class. The Presenter layer essentially binds the model to the controls in the View. The business logic and all important data is stored in the model.

Not the answer you're looking for? Browse other questions tagged or ask your own question.