DRAFT - DRAFT - DRAFT

Refactoring to Model-View-Controller - Part 2

Part 1
Part 2

Domain Model vs. Presentation Model

Our servlet has become three objects: a controller and two views. (See AlbumServlet4.java for the current state of the sample code. However, I do encourage you not to read the source, and instead to attempt these refactorings on your own.)

One might say that our task is now complete. We have objects representing the Controller (AlbumServlet), the View (AlbumPage and ErrorPage), and the Model (Album). However, there is still one more step we can take.

In some cases, it makes sense for an MVC cluster to have its very own data model, representing all and only the data required by its particular view.

In the present application, we have two separate data layers:

  1. The Domain Model, comprising all of the data available to the application (the User, Album, and Picture objects)
  2. The Album Page Presentation Model, comprising merely those data elements necessary to display a single instance of the Album page (including view-specific information like the current page number, or the order in which the pictures happen to be sorted)
  3. [expound on difference between presentation-specific and domain-general data]

In an abstract sense, the set of parameters passed in to the constructor (the Album and the Request), plus the locally-scoped information like the number of pictures to display per page, represent the Presentation Model for the Album View. For many cases, this level of abstraction will suffice. The View obtains the data it needs directly from the Domain, and/or stores its data inside itself in the form of instance variables.

However, there is value in extracting an explicit Presentation Model object. It makes the code more clearly communicate its intention by making clear exactly what data the view needs in order to accomplish its task.

Also, reducing the set of data elements to only those needed by the view helps clarify that class' dependencies, which in turn prepares you to reduce those dependencies.

Finally, extracting a separate Presentation Model class can improve the unit testing of your application. It is useful to be able to ask the following two questions separately:

  1. "given these parameters, what presentation data is generated?"
  2. "given these presentation data, what HTML is generated?"

Arguably, the current problem is too small to warrant this extra step. But for the sake of argument, let's see where it takes us.

Step 4: Extract Presentation Model Class

There are a few refactoring tricks to help the extraction procedure. A judicious combination of Encapsulate Field and Inline Method Body keeps the step size small. To move the Album object into the model:

  1. Self-Encapsulate Field
    String title = "Album: " + album.getName();
    =>
    String title = "Album: " + getAlbum().getName();
    
  2. Create the AlbumPageModel class
  3. Add a "model" field to AlbumPage
  4. Create an encapsulated field in the AlbumPageModel
    class AlbumPageModel
    {
        private Album album;
        private HttpServletRequest request;
        public Album getAlbum()
        {
            return album;
        }
    
  5. Replace the contents of AlbumPage.getAlbum with
    public Album getAlbum()
    {
        return getModel().getAlbum();
    }
    
  6. Inline AlbumPage.getAlbum()
    String title = "Album: " + getModel().getAlbum().getName();
    

For an alternate path to this same goal, see Introduce Parameter Object (p. 295 of Refactoring).

More Extractions

So far, the refactorings have been more-or-less mechanical. Let's take a look through the AlbumPage code (see AlbumServlet5.java) and see if there are any glaring duplications or misplaced methods.

Four things jumped out at me. (How many did you see?)

  1. getPageNumber does not print anything, and instead returns a value that is used by the display code. It probably belongs inside the model, not the view.
  2. Same thing for PICTURES_PER_PAGE. Let's turn it into a method and move it into the model as well.
  3. printPreviousAndNextLinks contains the horrid statement getModel().getAlbum().getPictures().size(). Let's extract it into a method (getTotalPictures) for now to isolate the indirection.

Strings of dereferences - - e.g. getModel().getAlbum().getPictures().size() -- are code smells. They signal improper encapsulation. The first step towards resolving this smell is to encapsulate it into a method. The next may be to move this method into the first object in the chain. In this case, the recently-extracted getTotalPictures method belongs in the model (to remove the first indirection). Once a method is in its proper object, you may notice that other indirections disappear as well.

Finally,

4. printPictureTable contains some fairly convoluted logic and looping code. How can we clean it up? Let's separate it into two phases: one which gathers all the pictures to print into a List, and one which prints every item in the list. Along the way, we will make copious use of our old friend, Extract Method.

Now that "calculate all the pictures we need" is reified as the printPictures method, it becomes clear that it needs to move into the View Model as well. You probably saw this coming.

A good general principle of MVC separation is,

The model calculates; the view iterates.
In other words, isolate the complexities of figuring out what to render, and let your GUI concentrate on how to render it.

The method printPreviousAndNextLinks is also a candidate for this sort of separation. I leave it as an exercise for the reader.

(AlbumServlet6.java captures our progress so far.)

Step 5: Disentangle

We now have three classes [3] that more-or-less implement our MVC pattern:

  1. AlbumServlet (Controller)
  2. AlbumPageModel (Data Model)
  3. AlbumPage (View)

Unfortunately, our job is not yet complete. The classes are still coupled too tightly.

One glaring problem is that the AlbumPage and AlbumPageModel refer to HttpServletRequest. This is an undesirable coupling. It is better to minimize library dependencies. This clears the path for using the object in a situation where we don't have access to a full-fledged Servlet engine - - for instance, inside an application which builds static Web pages, or inside a unit test.

We can resolve this by painstakingly examining each usage of HttpServletRequest, encapsulating it, and extracting it into either the constructor or, cutting to the chase, into the data model. So,

String pageParameter = getRequest().getParameter("page");
becomes
String pageParameter = getPageParameter();
where pageParameter is passed in from the outside and stored in a field. Before long, we have completely eliminated a library dependency, at the cost of a few constructor parameters.
public AlbumPageModel(Album album, HttpServletRequest request)
becomes
public AlbumPageModel(Album album, String pageParameter)

Another coupling is that the View generates its own model. While this is fine for the simple case, it is very useful to allow (or require) the model to be generated elsewhere and passed in. So the constructor

    public AlbumPage(Album album, HttpServletRequest request)
    {
        this.setModel(new AlbumPageModel(album, request.getParameter("page")));
    }
becomes
    public AlbumPage(AlbumPageModel model)
    {
        this.setModel(model);
    }
This has the side benefit of removing the dependency on HttpServletRequest from AlbumPage for free! It also, quite fortuitously, clarifies the role of the servlet as controller. The new servlet code (AlbumServlet7.java)
AlbumPageModel model = new AlbumPageModel(album, request.getParameter("page"));
new AlbumPage(model).printPage(out);
clearly communicates the roles, responsibilities, and sequence of actions. "I, the controller, will create a model; I will then create a view based on this model, and ask the view to print itself."

What's the Controller for?

As stated above, a controller is responsible for the interaction between model and view, and/or functions outside the scope of either. In the context of a servlet, this means

In an event-driven GUI context, the controller would have other responsibilities involving event listeners and the like, but the Web, for all its complexities, is actually a fairly simple application model. For instance, there is seldom any notification when the data changes. In a traditional GUI MVC system, a data change would send an event to refresh the view. In a typical Web application, the view remains static, and the user must reload or go to another page in order to see the updated data model.

Conclusion

There is more to do (there always is!) but for now, we have accomplished our stated goal of transforming a somewhat messy servlet into a cluster of objects separating the responsibilities of model, view, and controller. We have opened the door to further improvements in the code; instead of rooting around in a single class, we will be able to home in on the area that needs fixing; and once we get there, the code will be easier to understand (thanks to all our applications of Extract Method and Rename Symbol and the like).

But before we improve the class any further, we should really write a full set of unit tests. It turns out that cleanly separating model from view paves the way for a very interesting testing architecture. But that is a story for another day...

Notes

[1] "I've come to the conclusion that people forget about regular Java objects because they haven't got a fancy name - so while preparing for a talk Rebecca Parsons, Josh Mackenzie and I gave them one: POJO (Plain Old Java Object). A POJO domain model is easier to put together, quick to build, can run and test outside of an EJB container, and isn't dependent on EJB (maybe that's why EJB vendors don't encourage you to use them.)"

 - Martin Fowler, http://www.martinfowler.com/isa/domainModel.html

[2] or XML, or RMI, or java.io, or EJB, or JNDI, or JMS, or CORBA, or...

[3] We also have ErrorPage (View). The data model for an ErrorPage comprises a single string, the error message. As such, I feel no compelling need to extract it into a separate ErrorPageModel object. However, if you feel that step makes your code clearer, then be my guest.

References

http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/web-tier/web-tier5.html
Web-Tier Application Framework Design 

J2EE Architecture Approaches: Model-View-Controller Architecture
http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/app-arch/app-arch2.html#1106102

Refactoring Catalog
http://www.refactoring.com/catalog

IntelliJ IDEA
http://intellij.com/idea

Eclipse
http://eclipse.org/

Xrefactory for Emacs
http://xref-tech.com/speller/main.html

Purple Technology XP Links
http://www.purpletech.com/xp/

Acknowledgements

Thanks to Chris Lopez, Erik Hanson, and Hilary Nelson providing comments on earlier drafts of this article.

Alex Chaffee
Last modified: Fri Nov 1 13:51:33 PST 2002