COMP 310
Fall 2010

Lab03:  MVC implementation

Home  Info  Owlspace  Resources

Model-View-Controller Design Pattern

The MVC pattern enables us to decouple the view (the user interface portion) from the model (the back-end computations) in a large system. This decoupling will allow

The model and the view do not know anything about each other.   In fact, they should not know anything about each other.  (Why?)  This also means that you need to think carefully about what belongs in the model and what belongs in the view.  It's often easy to see that no user interface elements belong in the model, but it may not be so clear when first learning about user interfaces which pieces belong in the model instead of the view.

The communications between the model and the view consist of calling methods on interfaces to the rest of the system.   The model and view must provide public methods (services) that can be used to communicate to them.   Note that these services need not be standardized or abstracted, while the services they desire from the rest of the system must be abstracted into interfaces.   (Why?)

Connecting the Model and the View

The controller is the only part of the system that knows the concrete classes being used by the view and by the model.  So, one of the primary tasks of the controller is to connect the view and the model. This is done with adapters that translate the abstract interfaces into the concrete services that are provided by the view and the model.  For example, the view requires a particular adapter interface to access services in the model.  The controller would provide the view with a model adapter that translates the interface into the services provided by the actual model in use.

The model and/or the view need not only require a single adapter interface. You may use multiple adapter interfaces, as appropriate.  The following criteria can be used to determine if methods belong on the same or different adapter interfaces: 

A general discussion of the concept of adapters can be found here.  Actual implementations may vary slightly from this discussion, but will still conceptually be the same.

The Controller

The controller's primary job is to set up the entire system and connect it together.  To accomplish this, it must:

Notice that the controller has two fundamental tasks.  The first is to instantiate all of the components of the system.  The second is to actually start the system.  You can't instantiate both the model and the view at the same time.  Therefore, one of them will exist before the other.  The first component cannot start trying to request services from the other before it exists! So, you almost always need to start the components after they have been set up, resolving the circular dependence of the model needing the view and the view needing the model.

If the model or view is changed, then the controller will usually need to be changed as well, but any effect should be contained to only the controller.  In other words, if you change the view, you may have to change the controller, but you should not have to change the model.

The controller should be fairly simple and should never contain any "business logic".   If it becomes overly complicated, this is a sign that it is doing work that belongs in either the view or more likely, the model.   Is the model missing a module or a layer?

Solving the Circular Dependence Between the Model and the View

As previously mentioned, the controller starts the model and the view after it has instantiated both, so that they both exist before either requests services of the other.  Since the model and the view are decoupled, their constructors must take their adapters.  Using setters after they are created is not justifiable, as they must have the adapter to operate correctly.

So, the circular dependence still exists if you try to create adapter objects.  If you instantiate the view first, for instance, you will need to give it an adapter that has access to the model.  But the model hasn't been instantiated yet!

This problem can be solved by using anonymous inner classes within the controller.  These adapters can close over the fields of the controller that hold the model and view references.  When the adapters are created, these fields may not be instantiated yet.  But, they will be when the controller starts the execution of the model and the view!

Generality

While the MVC pattern is specific to user interfaces, the notions behind the design are generalizable to any large system of decoupled modules.  The "model" and the "view" could be anything that exports services that could be used by another component in the system.  It is sound design to encapsulate such services and isolate them from the rest of the system so that you can easily interchange them with new implementations that may or may not implement the same interface.

Using the MVC Pattern

In order to better understand how to implement the model, view, and controller, we are now going to take your homework 2 and move towards the MVC design.  In order to do so:

  1. Branch your homework 2 project in subclipse.

  2. Look at your model and find all of the places where the model needs access to something in the view.  Create a view adapter interface that provides methods that will give the model what it needs.  When designing the adapter interface, you should focus on what the model needs, not on what you know the view provides.  You may find it helpful to name your adapter interface something intuitive, i.e., IModel2ViewAdapter.

  3. Update the model constructor to take the appropriate view adapter(s) and modify the model to use the adapter to obtain whatever it needs from the view.

  4. Update (or write) the controller to create anonymous adapter(s) that appropriately access the view in the adapter methods.  If you do not have access to what you need in the view, you may have to modify your view to provide the appropriate services.

  5. Test your program.  It should work exactly as before.

  6. Perform a similar process on the view.

  7. Test your program again.  It should still work exactly as before.

When complete, your updated homework 2 should work exactly as before, but now the model should only access the view via adapters and the view should only access the model via adapters.  In this way, they are both decoupled from each other and are therefore not tied to any particular implementation.

For now, your adapters are probably fairly straightforward and don't really have to "adapt" much of anything.  As your continue to work on HW03, the interactions between the model and the view will become more complex, and it is likely that the adapters will not just have nearly identical methods to the service provider.


 


© 2010 by Stephen Wong and Scott Rixner