Friday, June 09, 2006

Tell, don’t ask – MVP – Biz Process Reference Data (Part I)

A good programming practice is “tell, don’t ask, which concerns class responsibility (btw, don’t try to apply this practice at home with your wife…). In these postings, I will outline how we use this rule to avoid excessive use of events in a model-view-presenter (MVP) WinForms module to get at the reference data of the current business process.

Reference data
is used in the model to annotate the business transaction work item data with extra information such as the customer number, account number, zip code, country, etc.

I have successfully used “tell, don’t ask” to refactor code that used events to get business process reference data (BPRD) values stored in a WinForms presenter control, to use a common, shared ‘current reference data’ object to contain and control the reference data. This also leads to a better design that follows the “single responsibility principle
and leads to better cohesion / separation of concerns in the presenter and the current-reference-data classes.

The WinForms module consists of a container user control that hosts a set of child user controls inside a tab control. In fact, one of the child controls is it self a tabbed container with further child controls. Before the refactoring, the reference data was stored in the presenter, much like the view-state data of the presentation-model
. Note that we use an adapted version of MVP due to our extensive use of WinForms databinding. Note also that we by constraint cannot use CAB in this project.

See J Aron Farr’s post at JadeTower
on ‘MVC, MVP, Presenter Model for more info about the different presentation logic patterns.

The old code contained several events for getting and setting the reference data defined in the view interface (one event pair per value). The child user controls (the views) would raise an event that the presenter would handle to provide the requested reference data value. Thus, a view would need ask for the data to get it and beg for the presenter to change it. After all, raising an event is not dictating (tell) something to happen, it is more like pleading (ask). This coding style started out small with just one value to get.

An example view interface (sorry about the Norwegian naming imposed on me):

Public Interface IKasseEkspedisjonView

#Region "Events"
Event KontonummerHent as EventHandler(Of GenericEventArgs(Of String))
Event KontonummerSett as EventHandler(Of GenericEventArgs(Of String))

Event BuntnummerHent as EventHandler(Of GenericEventArgs(Of Integer))
Event BuntnummerSett as EventHandler(Of GenericEventArgs(Of Integer))

Event EkspedisjonsnummerHent as EventHandler(Of GenericEventArgs(Of Integer))
Event EkspedisjonsnummerSett as EventHandler(Of GenericEventArgs(Of Integer))
#End Region


End Interface

The presenter would handle these events both to provide and to update the BPRD values stored in the presenter.

Note that I do not want the views to have a pointer/reference to the presenter. In addition, to keeps things simple, the container is also the presenter. The container naturally has pointers to all the views, added automatically by the Visual Studio designer when adding the child user controls to the tab control.

The events defined in the view interface had to be implemented by all child user controls using BPRD, and even using a user control base class to implement the events only once, it still would lead to a growing number of events and handlers as the number of reference data values increased. Likewise, the presenter would have to add handlers for each new event and for each new child user control added to the container.

It was time for a simpler solution to sharing the business process reference data. The refactoring involves creating a new value object to keep all the reference data and removing all of the get/set events from the view interface in favor of property changed events on the value object. The refactoring will also remove the need for the presenter to subscribe to BPRD events for each view instance.

Stay tuned for details about the refactored business-process-reference-data object in the upcoming part II.

[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.

No comments: