You would think the whole MVC thing is a snack for any modern software developer. For example I see MVC on a daily basis as part of Spring based MVC web applications etc so fixing my Java GUI mess is easy right? Well not as easy as I would like and forced me to re-examine what I think MVC is or isn't in the first place.
As I stated in my previous post - the objectives are to:
- Make the bulk of the program logic automatically testable.
- Not require the use of a GUI test tool to test the program logic. To do this I basically want to encapsulate the GUI as closely as possible (i.e minimize the logic inside the bits I can't test - sometimes this is referred to as a Humble Object http://martinfowler.com/eaaDev/uiArchs.html)
- Make the GUI code as simple as possible.
In the previous article I was looking at this specific problem to to with this view/controller for displaying the object managed by the application (boats, classes of boat, races, etc) but now I need to extend this to the rest of the UI. The other parts of the UI are mostly panels that display information about objects or are dialogs for modifying objects.
Initialization - Chickens and Eggs
One problem that came up with dialogs was that of composite views. In a traditional web MVC app the controller is invoked to display the view. The controller retrieves the data from a service as a model and then passes this to a view to edit. What happens if your view is actually a composite containing a sub-view and the sub-view is complex enough to have its own controller? Surely the view doesn't call the sub-view's controller to create the sub-view?
My solution was to have the view create the sub-view. The controller has to know about the internal structure of the view and so it creates a sub-controller to manage the sub-view. I can see scenarios where this will get sticky but it will do for now.
Presentation vs Logic
The next challenge is coming up with a consistent model for what is presentation and what is controller logic. The obvious thing to do here is anything that references SWT code is presentation but life is more complex than this...
Should the view update itself from the model directly or should it be updated by the controller? This article (The Humble Dialog http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf) argues that the view should be populated by the presenter and the view should be minimal. This generates a lot of getter/setter code as each field has to be exposed and then explicitly manipulated in the presenter. If this was done in the dialog it this would be much smaller.
I consider Combo boxes to be a good example of when this humble dialog model works well. Often you have a combo box with a list of possible selections and one either default value or (when editing an existing object) you have the current value. The code needs to populate the combo with the list of possibilities and mark one as selected. The code needs to handle the case where there are no possible values to select from, when there is no selection and therefore a default must be chosen and so on. There are a lot of things that need to be tested and if this code was placed in the GUI it would need to be done manually. Instead, exposing controls to add an item, mark the item as selected etc means this code can be done in the controller and tested. Is this view or controller code? I don't know but it works to move it.
Many dialogs (and other GUIs) will contain elements that are interlinked by the application logic. By this I mean controls that are affected by the value or selection state etc of other controls in the view. For example all the boats in a boat class (like Lasers) could start together or each division could start separately - if they start together then a control for choosing a single start time is active but otherwise a list of start times must be chosen.
Is this presentation code or business logic? It can be both as it may be logic that prevents an invalid choice being made but also this could be manifested in the GUI by a control being disabled so the user is aware of the restriction.
Its easy to implement in the GUI code but then you can't test it and sometimes the logic can be complex. Placing it in the controller does create some odd loops however as then the presentation notifies the controller of a selection and the controller must modify the GUI.
Compositing using Reusable bits
I have a couple of cases where the same bit of GUI appears in multiple views and as part of my move toward a more testable structure I wanted to make sure re-use of this kind was possible.
As an example I have a few places where a pair of combo boxes are displayed where one allows the user to choose the class of vessel and the second allows the user to choose which division of that class. So if you choose Laser you can then choose a division of 4.7, Radial and Full Rig.
There is logic involved in populating the combos that I'd like to re-use.
Using the Humble Dialog pattern, the presentation code is hidden behind an interface to allow the controller to be tested. The way this works is that:
- There is an interface for accessing the combo boxes.
- There is a re-usable panel that implements this interface and renders the combos. The implementation delegates to a controller when the user makes a selection
- There is a controller for the combos that changes their state via the interface when the selection state changes.
Then when you use this component (say in a dialog) you:
- Create methods for accessing the common-component interface (the combo box pair panel interface) from within the interface wrapping the dialog .
- The GUI component instantiates the re-usable component and provides access to it via the interface
- The controller for the GUI instantiates the combo-box panel controller. When the dialog is created it is passed the controller and it then retrieves the combo-panel controller from within that and passes it to the combo panel on construction.
Another consequence of the design approach here (and of Humble Dialog) is that now there is no direct interaction between the view and the model. Instead the view is populated by the controller (or presenter)
and the data in the view is scraped out by the presenter to populate the model.
I'm not sure how I feel a bit this. It seems too hard (generates too much code) but the benefits are clear for many cases (such as the combo example above).
Time will tell as I launch into the re-development.
and the data in the view is scraped out by the presenter to populate the model.
I'm not sure how I feel a bit this. It seems too hard (generates too much code) but the benefits are clear for many cases (such as the combo example above).
Time will tell as I launch into the re-development.