Constructing the View for the Reservations Page

As we examine different view technologies in this chapter, let's consider a simple dynamic page as an example. Last chapter we examined the controller code in the processSeatSelectionFormSubmission() method of the com.wrox.expertj2ee.ticket.web.TicketController class. This method handles submission of a form that allows the user to request the reservation of a number of seats of a specified type (such as "Premium Reserve") for a particular performance.

Information Presented and Required Formatting

Two views can result from processing this request:

  • A view displaying the newly created reservation, and inviting the user to proceed to purchase the seats (the "Show Reservation" view)

  • A view informing the user that there weren't enough free seats for the performance to satisfy their request (the "Not Enough Seats" view)

In this chapter, we'll use the "Show Reservation" view as an example. The "Not Enough Seats" view is completely distinct. Part of the point of using a controller to handle a request is that it enables us to choose one of several possible views depending on the outcome of the necessary business operations. The "Show Reservation" screen looks like this:

click to expand

It displays the following dynamic information:

  • The name of the show being performed and a graphic of the seating plan for that show

  • The date of the performance

  • The number of seats reserved, the lifetime of the reservation (how long before other users can reserve these seats if the current user fails to continue to purchase them), the names of the seats, and the cost of purchasing these seats, including the booking fee

Currency display and date and time display should be appropriate to the user's locale.

There is one variant of this view, which is displayed if there were enough seats to satisfy the user's request, but they are not adjacent. In this case, the user should be given the opportunity to abandon this reservation and try to book on another date. We need to add an additional section highlighting the potential problem and providing a link to try to book for another performance. Whatever view technology we use must be able to handle this simple conditional: this is the same view with a minor, presentational, difference, not a distinct view like the notEnoughSeats view.

click to expand

The Model Behind this View

The model returned by the controller's processSeatSelectionFormSubmission() method when it chooses the "Show Reservation" view contains three objects:

  • performance
    An object containing information about the performance and its parent show. This is of type com.wrox.expertj2ee.ticket.referencedata.Performance.

  • priceband
    An object displaying information such as the name (in the above screenshots, "Premium Reserve") and price of the type of seat requested. This is of type com.wrox.expertj2ee.ticket.referencedata.PriceBand.

  • reservation
    An object containing information about the user's reservation, such as the seats reserved the quoted price and whether or not the seats are adjacent. This is of type com.wrox.expertj2ee.ticket.boxoffice.Reservation.

The performance and priceband objects are reference data, shared among all users of the application. The reservation object is unique to the current user.

The three types are interfaces, not classes. The returned objects will be of implementation classes that expose methods that allow some of their state to be manipulated. This is best concealed from views, so we choose not to code views to the mutable classes (views should treat models as read-only). The use of interface-based design also allows the potential for different implementations.

Let's look at some of the important methods in each of these interfaces in turn. Please refer to the sample application source code for a complete listing of each.

Both the Performance and PriceBand interfaces are part of an abstract inheritance hierarchy based on com.wrox.expertj2ee.ticket.reference.ReferenceItem, which exposes a numeric id and a name or code as follows:

    public interface ReferenceItem extends Serializable {      int getId();      String getName();    } 

It's worthwhile ensuring this basic commonality between reference data objects as it saves a little code in concrete implementations, which use a parallel inheritance hierarchy, and makes it easy to treat objects consistently. For example, reference items can be indexed by id whatever their subtype, to allow for rapid lookup.

The following UML class diagram illustrates the inheritance hierarchy:

click to expand

The following diagram shows how reference data instances of the types shown above are assembled into a tree at run time. Each object provides methods to navigate downwards, through its children (for the sake of simplicity, I've only expanded one Show and one Performance:

click to expand

The Performance interface, part of the model for the "Show Reservation" view, extends ReferenceItem to expose a link to the parent Show object, as well as the performance's date and time and a list of PriceBand objects for that performance:

    public interface Performance extends ReferenceItem {      Show getShow();      Date getWhen();      List getPriceBands();    } 

PriceBand objects add seat price to the information about seat types such as the name or code (such as AA) and description (such as Premium Reserve) exposed by the SeatType interface. The following is a complete listing of these two simple interfaces:

    public interface SeatType extends ReferenceItem {      int getSeatTypeId();      String getDescription();    }    public interface PriceBand extends SeatType {      public double getPrice();    } 

The com.wrox.expert.j2ee.ticket.boxoffice.Reservation interface exposes user-specific information. A Reservation object is created when seats are reserved for a user, and contains a reference to the ReservationRequest object (a command generated by the user) that caused the reservation to be made. A Reservation isn't reference data, but dynamic data created when a user successfully completes the first step of the-booking process. Note that a Reservation is serializable. We will need to put Reservation objects in a user session: we need to ensure that this is possible in a clustered environment.

I've highlighted the methods relevant to views:

    public interface Reservation extends Serializable { 

The following method returns an array of the Seat objects reserved for the user:

    Seat[] getSeats(); 

The following method indicates whether or not these seats are adjacent. As we've seen, presentation logic may depend on this information, but determining whether or not a set of seats is adjacent isn't presentation logic, as it will involve understanding of the relevant seating plan:

    boolean seatsAreAdjacent(); 
    int getMinutesReservationWillBeValid();    double getTotalPrice();    ReservationRequest getQuoteRequest(); 

The following methods are used only by controllers:

    long getTimestamp();    String getReference();    boolean isReserved();    boolean satisfiesRequest (ReservationRequest r);    } 

Please refer to these listings as necessary to understand the view code shown below.

None of these model objects is web-specific. In fact they're not even UI-specific, which means that we can easily write test harnesses that check for expected results, and could easily use these objects as the basis of a web services interface.

Model Principles

The design of these models raises some important points relating to all models that may be used in web applications:

  • Models should be JavaBeans, to ensure that they provide the maximum value to views. Note that all the methods exposed by these classes for display follow JavaBeans naming patterns: for example, the Reservation interface exposes a totalPrice property via a getTotalPrice() property getter, not a totalPrice() method. If we expose methods, rather than bean properties, JSP and some other view technologies may have limited ability to access model information.

  • Some simple calculations are done in the model, even though they could be done by the view. For example, it's possible to work out the number of minutes before a Reservation object expires from the holdTill property of the ReservationRequest object it contains and the system date. Thus the minutesReservationWillBeValid property of the Reservation object is, strictly speaking, redundant.

    However, not all view technologies may allow easy access to the system date or implementation of the necessary calculation. If there were to be several views of this model object, each would need to implement its own version of the calculation - redundancy that's likely to have serious implications. Model design is a very different problem to relational database design: there are no prizes for avoiding redundancy. However, processing that involves locale-specific information, such as number formatting, normally should be done in views, because this is purely a presentational issue.

  • Similarly, there's no culling of redundant information in the model. For example, the PriceBand object is also, strictly speaking, redundant. It would be possible for a view to navigate to the relevant PriceBand object among all the PriceBand children of the Performance object by comparing each seat type id to the seatTypeId request parameter resulting from the form submission.

    However, there are good reasons for supplying this "redundant" information in the view. Accessing request parameters trespass on control logic (inappropriate in views). Furthermore, not all views can access request parameters: for example, a JSP view can, while an XSLT stylesheet cannot. Views should be able to get all the information they require from the data model, without needing to check the request or any user session. As the PriceBand object in the model is a reference to one of the PriceBand objects held by the Performance object, we've merely moved the lookup inside the controller, rather than wasted memory on an unnecessary object.

Since the web application framework introduced in Chapter 12 enables us to return a Map, rather than a single object, as model, we don't need to create a single container object to hold all this information in one object. Such an object would be unique to this page, so by not needing to create one we've avoided creating what would be a web interface-specific object.

Important 

Model objects used in web interfaces don't need to - and should not - be web-specific. For example, they should never expose markup or other formatting information.

Following a few golden rules will ensure that model objects provide maximum value to views, whatever the view technology used:

  • Model objects should be JavaBeans
    Views should be able to get all the data they need by accessing bean properties and should not need to invoke methods on model objects.

  • Model objects should be smart so that the views that display them can be dumb
    This doesn't mean that model objects should perform any data retrieval; this should have been done before the model was returned, by business objects invoked by web tier controllers. It means, however, that models should go the extra mile to make things easy for views; for example, by performing non-trivial calculations and exposing the results, even if the inputs to the calculation are available to views from the model's other bean properties.

  • Models should expose all the information required by the view
    There should be no need - and therefore, no excuse - for views to access HttpRequest or HttpSession objects. The use of incomplete models will constrain view implementations to those technologies, such as JSP, that facilitate access to the underlying Servlet API objects.

  • Views, not models, should usually handle locale-specific issues
    Model objects are not necessarily display-specific, and so shouldn't need to handle localization issues.

Sometimes the second of these four rules can best be followed by creating an adapter bean: a value object that caches and exposes all model data needed by one or more views in a way that makes it simple to access. For example, an adapter might expose via bean properties data retrieved by method calls on several non-bean objects with complex interfaces. In such cases, the adapter simplifies the data interface available to views and suppresses unwanted, distracting information. We have no need for this adapter approach in the sample application, as domain objects such as the Reservation object are naturally suited to use as models without any design compromises being necessary.

If an adapter bean is used, it should be created by the relevant controller, not the view. Although it is easy for JSP pages to create adapter beans, and this approach is sometimes advocated, this breaks the principle of view substitutability.



Expert One-on-One J2EE Design and Development
Microsoft Office PowerPoint 2007 On Demand
ISBN: B0085SG5O4
EAN: 2147483647
Year: 2005
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net