Question

When an event fires that should act on the focused control (not the control that fired the event), how do you handle this in the Model-GUI-Mediator (MGM) pattern? I suspect this question also applies to the closely related MVP pattern if you have more than one model.

I titled this 'delegating events' because I think the mediator for the event-originating control has to delegate the event to the mediator for the selected control (or to its model) - but the question is, 'how?'

Background

The MGM pattern is a bit like MVP Passive View, except that you have a separate 'mediator' for each control (rather than one presenter per form). A control's events are all handled by its mediator, and the mediator calls methods of its particular model in response. The mediator is an observer of the model and updates its control when the model changes. It's particularly suitable in a RAD environment where your controls don't have data binding and you don't want to subclass them. It also has the advantage that there's very little boilerplate code to hook up events, unlike Passive View. Here are two more detailed descriptions:

Problem Example

  1. A form contains a number of controls that provide views on a number of model objects. Only one of these views can be selected at a time. (Imagine a multi-document editor if you want something concrete.)

  2. A user invokes a command from a menu. This calls the Execute method in the particular menu item's mediator object. (The menu item is a control, so it has a corresponding mediator.)

  3. The command is supposed to perform an action on the currently selected control.

  4. So the menu item's mediator needs to find either the mediator belonging to the selected control and delegate the action there, or it needs to find the model associated with the selected control's mediator and call that directly.

But how does the menu item's mediator find the selected control's mediator?

In MGM, mediator objects are supposed to be self-contained, so don't know about other mediator objects. The controls are not allowed to know anything about the models (to maintain clear separation of concerns). The only thing the controls know about their mediators are the event handlers.

Hacky workaround

The best I've come up with so far is to examine an event field in the selected control, which will be an object method pointer to that control's mediator. In Delphi I can cast this to a TMethod and hence extract the object pointer for the control's mediator. I can then cast this to the mediator's type and call the required method.

But this seems to rely heavily on a language feature (TMethod) and also creates dependencies between the mediator classes.

Perhaps I'm on the wrong track entirely…

(P.S. Could someone with more rep than me please create a "model-gui-mediator" tag? Thanks.)

Was it helpful?

Solution

Just in case anyone views this question in the future, I found the 'official' answer.

In MGM (and Passive View), the model is supposed to contain all necessary state information. So in this case the application model would keep track of which is the focused control.

In the example of a multi-document editor, you'd have an application model that handles the various menu commands and keeps track of which editor currently has focus. It's easy then to delegate work to the editor model corresponding to the focused editor.

(This isn't actually the solution I adopted. The state information was already contained in the GUI and I didn't want to duplicate this in the model - it always seems clumsy keeping the same information in more than one place and has the highly undesirable potential for introducing inconsistencies. This is perhaps one weakness of patterns such as MGM and Passive View. Anyway, I went with the 'hacky' workaround I described in the question.)


Just as a footnote, another way to handle this kind of situation – where the control handles a lot of functionality and state that you don't want to duplicate in the model but to which the model needs access – is to define events in the model that it can call when it needs to access said functionality/state. Using events keeps mediator dependencies out of the model, which is a basic requirement and raison-d'etre for MGM.

For example, if a control has the ability to save its content, define an OnSave event in the model and have the mediator hook up a handler that invokes the save functionality in the control. The model's Save method then just calls the event, and the control takes care of the implementation without the model having to know anything about it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top