Wednesday, June 14, 2006

Model Reference Data = Observer pattern + Reference Data + MVP (Part II)

In part I, I outlined how we use common business process reference data (BPRD) in our WinForms user controls contained in a model-view-presenter application. Before the refactoring, the presenter was used to hold and provide the data, and also had the responsibility of notifying the applicable views that the reference data had changed. This lead to a rather unreadable and unmaintainable design as the set of events and views got bigger

All who have developed a tabbed wizard control know that using a common data object shared between all the tabs is a good design. The refactored design is analogous to this, in addition to employing the Observer pattern. The WinForm user control container module described in Part I is just an advanced, decoupled MVP implementation of a multi-tabbed user interface module.

The refactoring of the module comprises these steps:
1. Extract all common reference data into a new ‘subject’ class as private members.
2. Expose the reference data as public properties.
3. Add event definitions to the applicable properties to notify ‘observers’ of changes to the ‘subject’.
4. Add a property to the presenter to hold an instance of the new BPRD object type.
5. Add a property to the view interface, of the new BPRD object type.
6. Add code in the presenter to create an instance of the reference data (BPRD).
7. Add code in the presenter to set the BPRD property of each of the views.
8. Add event handlers in the presenter and views (observers) to subscribe to applicable notifications from the new BPRD object.

In the new design, all views hold a reference (pointer) to a common, shared instance of the BPRD object. They all change the common object and they all can subscribe to whatever event they need to get notified of. Thus, the presenter need not contain any code to handle BPRD events to then invoke methods on the views. This leads to a much simpler presenter. The views are also somewhat simpler, as the view interface now contains less to implement.

The refactored view interface looks like this:

public
interface IEkspederingView

{

//note how all the get/set events from part I are gone

//property pointing to the current common, shared BPRD

EkspederingFellesData GjeldendeEkspederingFellesData{get; set;}

...

}


The new business process reference data class looks like this:

public class EkspederingFellesData

{

public EventHandler<EventArgs> KontonummerSatt;


public string Kontonummer

{

get { return _kontonummer; }

set

{

_kontonummer = value;

//subject: notify subscribers of change

if (KontonummerSatt != null) this.KontonummerSatt(this, new EventArgs());

}

}

private string _kontonummer;

...

}


The presenter looks like this:

public class EkspederingPresenter

{

public EkspederingPresenter()

{

//create and load the BPRD

_currentReferenceData = new EkspederingFellesData();


//add the current BPRD to all the views

_innbetalingView = new InnbetalingView();

_innbetalingView.GjeldendeEkspederingFellesData = _currentReferenceData;

}


public EkspederingFellesData GjeldendeEkspederingFellesData

{

get { return _currentReferenceData; }

}

private EkspederingFellesData _currentReferenceData;


private InnbetalingView _innbetalingView;

...

}


And finally, one of the views looks like this:

public class InnbetalingView : IEkspederingView

{

public InnbetalingView()

{

//observer: add event handlers to subscribe to notifications to BPRD

this.GjeldendeEkspederingFellesData.KontonummerSatt += new EventHandler<EventArgs>(OnKontonummerSatt);

}


public EkspederingFellesData GjeldendeEkspederingFellesData

{

get { ... }

set{...}

}


public void OnKontonummerSatt(object sender, EventArgs e)

{

//do function X13

}

...

}


The reference data value object is the ‘Subject’ of the Observer pattern, while the ‘Observer’ objects are the presenter and (possibly) the views. Thus, the reference data ‘Subject’ object (don’t get confused) is in fact a ‘Model’ object, although not a domain object such as a customer or an order.

If this combination of the observer pattern, the MVP pattern and a reference data object should have a name, I would call it the “Model Reference Data” pattern, the subject/model being the common, shared and observed reference data.

[UPDATE] Martin Fowler has since retired MVP and replaced it with the Supervising Presenter pattern. The new pattern is actually very like our adapted MVP usage, as it fits better with WinForms data binding.

2 comments:

Anonymous said...

Oh shait! koder dere på *norsk* !?

--larsw

Kjell-Sverre Jerijærvi said...

Jepp, kunden ville ikke oversette sine domene-termer til engelsk (bank/finans), og siden vi bare bidrar med "kjøtt" på prosjektet, så bøyer vi oss for dem (a.k.a konsulenter) =D