In this Blog I will introduce a simple hierarchical MVC implementation. This is a follow-up of my MVC / MVP article series, and I will refer to source-code from passed Blogs. In particular I will reuse the temperature-MVC inside a parent MVC that lets input a location of the temperature-measurement. This again is a Vaadin application.
You may have noticed that I always use very simple examples. This is to keep source-code short and understandable, and to focus on the basic concepts. The real world MVC implementations may be more complicated. But what works in little will also work in big!
Hierarchical MVC (HMVC) should help us to reuse ready-made MVC-instances inside a superordinate MVC. But this puts up a number of questions:
There is not very much on the web about hierarchical MVC. I just remember a Java project in the late Nineties that facilitated this, it was called Scope, still available by the time of this writing.
When thinking of a hierarchical MVC, we might imagine that just controllers make up the hierarchy, and thus are the MVC-capsule:
But reality shows that, in many cases, we have parallel hierarchies in all three of model, view and controller:
Following figure tries to make that more explicit:
Here comes my example application. Besides the inputs for temperature in Celsius and Fahrenheit it provides a text-field for entering the location of the temperature-measurement. Here is a screenshot of the initial browser UI:
The "Reset" button is not part of the MVC, it is just to demonstrate how data-models can be switched also in a HMVC.
Presentation logic specifies that when there is no location in the text-field, the temperature fields should be disabled, which looks like this:
Please refer to my passed Blogs to find the classes for the reused temperature-MVC. Following is the package-structure of the new MVC that embeds it:
You can test this using the ExamplesUi
servlet that can render different Vaadin Components. In a terminal window, change to the Vaadin directory where pom.xml
resides, and enter
mvn package jetty:run
to build the project and start a Jetty web server. Then, in browser address line, enter
http://localhost:8080/ExamplesUi?class=vaadin.examples.mvc.hierarchical.viewimpl.Demo
But first populate the packages with following Java classes!
1 | package vaadin.examples.mvc.hierarchical.viewimpl; |
The demo resides in the windowing-system dependent viewimpl
package, because it needs to appear in a Vaadin application. It builds together the MVC and sets initial values into it. The "Reset" button will set an empty new MeasurementStationModel
into the HMVC, which should clear all fields, including the temperature fields of the nested MVC.
1 | package vaadin.examples.mvc.hierarchical; |
Besides the location
field, this model just contains a TemperatureModel
. Together they make up the model-hierarchy.
The model exposes its sub-model publicly, it does not wrap it like the Law of Demeter would require.
1 | package vaadin.examples.mvc.hierarchical; |
The listener mechanism provides just one event which is fired when the user presses ENTER in the location-field.
Besides the stereotype getAddableComponent()
and setModel()
methods, also the view-interface outlines the hierarchy by exposing getTemperatureView()
publicly. The MeasurementStationController
will call it to construct its nested TemperatureController
, because that requires a non-null view instance, and a controller can not instantiate a windowing-system dependent class!
Finally there is a little presentation logic that disables the temperature-view when the location is empty. Any such logic will materialize in the view-interface, like enableTemperatureView()
does. It must be exposed to be called by the presenter. A passive view is not allowed to handle this internally!
1 | package vaadin.examples.mvc.hierarchical; |
In the constructor, the controller adds itself as listener to the view. Then it builds a nested TemperatureController
to dispatch the temperature-field events. This represents the last of the three parallel hierarchies. Other than the model and the view, the controller does not expose the sub-controller publicly, it is not needed.
The setModel()
implementation does two things. First it delegates the model to its own view. Second it connects the model hierarchy with the view hierarchy by setting the temperature-controller's model from MeasurementStationModel.getTemperatureModel()
.
The inputLocation()
event callback sets the new location to the model, and then performs the mentioned presentation-logic by setting the temperature fields enabled according to the new location
value.
1 | package vaadin.examples.mvc.hierarchical.viewimpl; |
The Vaadin view implementation is quite simple. In the constructor the layout is built together. The listener mechanism delegates to a Vaadin ValueChangeListener
.
Mind that modelChanged()
does not set the temperature-view's model, i.e. it binds only its own data, not those of its sub-view. The sub-view binding must be done by the responsible temperature-controller, first to prevent the temperature-view seeing another model than its controller, and second to avoid the binding being done twice (once by controller, once by view).
For simplicity I left out the view-implementation classes. They would have the MVC-typical association from view to model, which is not visible here.
All remaining Temperature*
classes you find in my recent Blogs.
Most remarkable may be that the data-binding of the sub-view is done by the controller, not the view. This is like in MVP, where the presenter maps the data from model to view, and the view does not know the model directly.
Making MVC hierarchical opens a lot of possibilities. A simple way is to use parallel hierarchies, that way you can cleanly separate the windowing-system from presentation-logic. The disadvantage of parallel hierarchies is obvious: when changing the model-hierarchy, also the view-hierarchy will have to be changed. But isn't this normal in MVC?
In my next Blog I will add a unit test for HMVC.
ɔ⃝ Fritz Ritzberger, 2017-03-05