ViewHelper Pattern
Context
The system creates presentation content, which requires processing of dynamic business data.
Problem
Presentation tier changes occur often and are difficult to develop and maintain when business data access logic and presentation formatting logic are interwoven. This makes the system less flexible, less reusable, and generally less resilient to change.
Intermingling the business and systems logic with the view processing reduces modularity and also provides a poor separation of roles among Web production and software development teams.
Forces
§ Embedding business logic in the view promotes a copy-and-paste type of reuse. This causes maintenance problems and bugs because a piece of logic is reused in the same or different view by simply duplicating it in the new location.
§ It is desirable to promote a clean separation of labor by having different individuals fulfill the roles of software developer and Web production team member.
§ One view is commonly used to respond to a particular business request.
Solution
A view contains formatting code, delegating its processing responsibilities to its helper classes, implemented as JavaBeans or custom tags. Helpers also store the view's intermediate data model and serve as business data adapters.
There are multiple strategies for implementing the view component. The JSP View Strategy suggests using a JSP as the view component. This is the preferred strategy, and it is the one most commonly used. The other principal strategy is the Servlet View Strategy, which utilizes a servlet as the view (see the section "Strategies" for more information).
Encapsulating business logic in a helper instead of a view makes our application more modular and facilitates component reuse. Multiple clients, such as controllers and views, may leverage the same helper to retrieve and adapt similar model state for presentation in multiple ways. The only way to reuse logic embedded in a view is by copying and pasting it elsewhere. Furthermore, copy-and-paste duplication makes a system harder to maintain, since the same bug potentially needs to be corrected in multiple places.
A signal that one may need to apply this pattern to existing code is when scriptlet code dominates the JSP view. The overriding goal when applying this pattern, then, is the partitioning of business logic outside of the view. While some logic is best encapsulated within helper objects, other logic is better placed in a centralized component that sits in front of the views and the helpers-this might include logic that is common across multiple requests, such as authentication checks or logging services, for example. Refer to the "Intercepting Filter" on page 4 and "Front Controller" on page 21 for more information on these issues.
If a separate controller is not employed in the architecture, or is not used to handle all requests, then the view component becomes the initial contact point for handling some requests. For certain requests, particularly those involving minimal processing, this scenario works fine. Typically, this situation occurs for pages that are based on static information, such as the first of a set of pages that will be served to a user to gather some information (see "Dispatcher View" on page 232). Additionally, this scenario occurs in some cases when a mechanism is employed to create composite pages (see "Composite View" on page 203).
The View Helper pattern focuses on recommending ways to partition your application responsibilities. For related discussions about issues dealing with directing client requests directly to a view, please refer to the section "Dispatcher View" on page 232.
Structure
Figure 7.11 is the class diagram representing the View Helper pattern.
Figure 7.11 View Helper class diagram
Participants and Responsibilities
Figure 7.12 shows the sequence diagram representing the View Helper pattern. A controller typically mediates between the client and the view. In some cases, though, a controller is not used and the view becomes the initial contact point for handling the request. (Also, see Dispatcher View pattern.)
Figure 7.12 View Helper sequence diagram
As noted in the class diagram, there may be no helpers associated with a view. In this simple case, the page may be entirely static or include very small amounts of inline scriptlet code. This scenario is described in the sequence diagram in Figure 7.13.
Figure 7.13 View Helper simple sequence
diagram
View
A view represents and displays information to the client. The information that is used in a dynamic display is retrieved from a model. Helpers support views by encapsulating and adapting a model for use in a display.
Helper
A helper is responsible for helping a view or controller complete its processing. Thus, helpers have numerous responsibilities, including gathering data required by the view and storing this intermediate model, in which case the helper is sometimes referred to as a value bean. Additionally, helpers may adapt this data model for use by the view. Helpers can service requests for data from the view by simply providing access to the raw data or by formatting the data as Web content.
A view may work with any number of helpers, which are typically implemented as JavaBeans (JSP 1.0+) and custom tags (JSP 1.1+). Additionally, a helper may represent a Command object, a delegate (see "Business Delegate" on page 248), or an XSL Transformer, which is used in combination with a stylesheet to adapt and convert the model into the appropriate form.
ValueBean
A value bean is another name for a helper that is responsible for holding intermediate model state for use by a view. A typical case, as shown in the sequence diagram in Figure 7.12, has the business service returning a value bean in response to a request. In this case, ValueBean fulfills the role of a Transfer Object (see "Transfer Object" on page 261).
BusinessService
The business service is a role that is fulfilled by the service the client is seeking to access. Typically, the business service is accessed via a Business delegate. The business delegate's role is to provide control and protection for the business service (see the "Business Delegate" on page 248).
Strategies
JSP View Strategy
The JSP View Strategy suggests using a JSP as the view component. This is the preferred strategy to the Servlet View Strategy. While it is semantically equivalent to the Servlet View Strategy, it is a more elegant solution and is more commonly used. Views are the domain of Web designers, who prefer markup to Java code. Example 7.17 shows a code sample for this strategy. The excerpt is from a source file called welcome.jsp, to which a servlet controller dispatches after placing the WelcomeHelper JavaBean in request scope.
Example 7.17 JSP View Strategy Sample Code
<jsp:useBean id="welcomeHelper" scope="request" class="corepatterns.util.WelcomeHelper" />
<HTML> <BODY bgcolor="FFFFFF"> <% if (welcomeHelper.nameExists()) { %> <center><H3> Welcome <b> <jsp:getProperty name="welcomeHelper" property="name" /> </b><br><br> </H3></center> <% } %>
<H4><center>Glad you are visiting our site!</center></H4>
</BODY> </HTML> |
The alternative Servlet View Strategy is typically implemented by embedding HTML markup directly within Java Servlet code. Intermingling Java code and markup tags creates a poor separation of user roles within a project and increases the dependencies on the same resources among multiple members of different teams. When an individual works on a template containing unfamiliar code or tags, it increases the likelihood of an accidental change introducing problems into the system. There is also a reduction in work environment efficiency (too many people sharing the same physical resource) and an increase in source control management complexity.
These problems are more likely to occur in larger enterprise environments that have more complicated system requirements and that use teams of developers. They are less likely to occur with small systems that have simple business requirements and use few developers, because the same individual may likely fill the roles mentioned above. However, keep in mind that projects often start small-with simple requirements and few developers-but may ultimately evolve to become sophisticated enough to benefit from these suggestions.
Servlet View Strategy
The Servlet View Strategy utilizes a servlet as the view. It is semantically equivalent to the preferred JSP View Strategy. However, the Servlet View Strategy, as seen in Example 7.18, is often more cumbersome for the software development and Web production teams because it embeds markup tags directly within the Java code. When tags are embedded within the code, the view template is more difficult to update and modify.
Example 7.18 Servlet View Strategy Sample Code
public class Controller extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); }
public void destroy() { }
/** Processes requests for both HTTP * <code>GET</code> and <code>POST</code> methods. * @param request servlet request * @param response servlet response */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { String title = "Servlet View Strategy"; try { response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter(); out.println("<html><title>"+title+"</title>"); out.println("<body>"); out.println("<h2><center>Employees List</h2>"); EmployeeDelegate delegate = new EmployeeDelegate();
/** ApplicationResources provides a simple API * for retrieving constants and other * preconfigured values**/ Iterator employees = delegate.getEmployees( ApplicationResources.getInstance(). getAllDepartments()); out.println("<table border=2>"); out.println("<tr><th>First Name</th>" + "<th>Last Name</th>" + "<th>Designation</th><th>Id</th></tr>"); while (employees.hasNext()) { out.println("<tr>"); EmployeeTO emp = (EmployeeTO)employees.next(); out.println("<td>"+emp.getFirstName()+ "</td>"); out.println("<td>"+emp.getLastName()+ "</td>"); out.println("<td>"+emp.getDesignation()+ "</td>"); out.println("<td>"+emp.getId()+"</td>"); out.println("</tr>"); } out.println("</table>"); out.println("<br><br>"); out.println("</body>"); out.println("</html>"); out.close(); } catch (Exception e) { LogManager.logMessage("Handle this exception", e.getMessage() ); } }
/** Handles the HTTP <code>GET</code> method. * @param request servlet request * @param response servlet response */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); }
/** Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); }
/** Returns a short description of the servlet. */ public String getServletInfo() { return "Example of Servlet View. " + "JSP View is preferable."; }
/** dispatcher method **/ protected void dispatch(HttpServletRequest request, HttpServletResponse response, String page) throws javax.servlet.ServletException, java.io.IOException { RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(page); dispatcher.forward(request, response); } } |
JavaBean Helper Strategy
The helper is implemented as a JavaBean. Using helpers results in a cleaner separation of the view from the business processing in an application, since business logic is factored out of the view and into the helper component. In this case the business logic is encapsulated in a JavaBean, which aids in content retrieval and adapts and stores the model for use by the view.
Using the JavaBean Helper Strategy requires less upfront work than does the Custom Tag Helper Strategy, since JavaBeans are more easily constructed and integrated into a JSP environment. Additionally, even novice developers understand JavaBeans. This strategy is also easier from a manageability standpoint, since the only resulting artifacts are the completed JavaBeans. An example of this strategy is shown in Example 7.19.
Example 7.19 JavaBean Helper Strategy Code Sample
<jsp:useBean id="welcomeHelper" scope="request" class="corepatterns.util.WelcomeHelper" />
<HTML> <BODY bgcolor="FFFFFF"> <% if (welcomeHelper.nameExists()) { %> <center><H3> Welcome <b> <jsp:getProperty name="welcomeHelper" property="name" /> </b><br><br> </H3></center> <% } %>
<H4><center>Glad you are visiting our site!</center></H4>
</BODY> </HTML> |
Custom Tag Helper Strategy
The helper is implemented as a custom tag (JSP 1.1+ only). Using helpers results in a cleaner separation of the view from the business processing in an application, since business logic is factored out of the view and into the helper component. In this case the business logic is encapsulated in a custom tag component, which may aid in content retrieval and adapts the model for use by the view.
Using the Custom Tag Helper Strategy requires more upfront work than does the JavaBean Helper Strategy, since custom tag development is moderately complicated relative to JavaBean development. Not only is there more complexity in the development process, but there is much more complexity with respect to integrating and managing the completed tags. To use this strategy, the environment must be configured with numerous generated artifacts, including the tag itself, a tag library descriptor, and configuration files. An excerpt of a JSP View using this strategy is shown in Example 7.20.
Example 7.20 Custom Tag Helper Strategy Sample Code
<%@ taglib uri="/web-INF/corepatternstaglibrary.tld" prefix="corepatterns" %> <html> <head><title>Employee List</title></head> <body>
<div align="center"> <h3> List of employees in <corepatterns:department attribute="id"/> department - Using Custom Tag Helper Strategy. </h3> <table border="1" > <tr> <th> First Name </th> <th> Last Name </th> <th> Designation </th> <th> Employee Id </th> <th> Tax Deductibles </th> <th> Performance Remarks </th> <th> Yearly Salary</th> </tr> <corepatterns:employeelist id="employeelist_key"> <tr> <td><corepatterns:employee attribute="FirstName"/> </td> <td><corepatterns:employee attribute= "LastName"/></td> <td><corepatterns:employee attribute= "Designation"/> </td> <td><corepatterns:employee attribute= "Id"/></td> <td><corepatterns:employee attribute="NoOfDeductibles"/></td> <td><corepatterns:employee attribute="PerformanceRemarks"/></td> <td><corepatterns:employee attribute="YearlySalary"/></td> <td> </tr> </corepatterns:employeelist> </table> </div> </body> </html> |
Business Delegate as Helper Strategy
Helper components often make distributed invocations to the business tier. We suggest using a business delegate in order to hide the underlying implementation details of this request, such that the helper simply invokes a business service without knowing details about its physical implementation and distribution (see "Business Delegate" on page 248).
Both a helper and a business delegate may be implemented as a JavaBean. Thus, one could combine the notion of the helper component and the business delegate and implement the business delegate as a specialized type of helper. One major distinction between a helper and a business delegate, though, is as follows: A helper component is written by a developer working in the presentation tier, while the delegate is typically written by a developer working on the services in the business tier. (Note: The delegate may also be provided as part of a framework.) Thus, this strategy is as much about who actually writes the delegate as it is about the implementation. If there is some overlap in developer roles, then the business delegate as helper is a strategy to consider.
Example 7.21 Business Delegate as Helper Strategy Sample Code
/**A servlet delegates to a command object helper, as shown in the following excerpt:**/ String resultPage = command.execute(request, response);
/**The command object helper uses the business delegate, which is simply implemented as another JavaBean helper, as shown in the following excerpt:**/
AccountDelegate accountDelegate = new AccountDelegate(); |
Note on Helpers:
JavaBean helpers are best used for aiding in content retrieval and storing and adapting the model for the view. JavaBean helpers are often used as command objects as well.
Like JavaBean helpers, custom tag helpers may fulfill each of these roles, except for acting as a command object. Unlike JavaBean helpers, custom tag helpers are well suited to control flow and iteration within a view. Custom tag helpers used in this way encapsulate logic that would otherwise be embedded directly within the JSP as scriptlet code. Another area where custom tag helpers are preferred is formatting raw data for display. A custom tag is able to iterate over a collection of results, format those results into an HTML table, and embed the table within a JSP View without requiring any Java Scriptlet code.
Consider an example in which a Web client is requesting account information from a system, as shown in Figure 7.14. There are five helpers shown in this diagram. The four JavaBean helpers are the AccountCommand object, Account object, AccountDAO, and AccountDetails. The sole custom tag helper is the TableFormatter object.
Figure 7.14 Using helpers
The controller handles the request. It creates or looks up the appropriate command object, which is implemented as a JavaBean helper. In this case, it is a command object that processes requests for account information. The controller invokes the Command object, which asks a JavaBean Account object for information about the account. The Account object invokes the business service, asking for these details, which are returned in the form of a Transfer Object (see "Transfer Object" on page 261), implemented as a JavaBean.
So how does the Account object access the business services? Let us examine two cases, one simple and the other more sophisticated. In the simple case, imagine that a project is taking a phased approach, phasing Enterprise JavaBeans (EJB) into the business tier over time. Assume at the moment that the database is being accessed via JDBC calls from the presentation tier. In this case, the Account object uses a Data Access object (see "Data Access Object" on page 390), hiding the underlying implementation details of accessing the database. The Data Access object knows what SQL queries are necessary to retrieve the information. These details are hidden from the rest of the application, reducing coupling and making each component more modular and reusable. This case is described in the previous sequence diagram.
When the architecture becomes more sophisticated, and EJB is introduced in the business tier, then the Data Access object is replaced with a business delegate (see "Business Delegate" on page 248), typically written by the developers of the business service. The delegate hides the implementation details of EJB lookup, invocation, and exception handling from its client. It might also improve performance by providing caching services. Again, the object reduces coupling between tiers, improving the reusability and modularity of the various components. Regardless of the specific implementation of this object, its interface may remain unchanged during this transition. Figure 7.15 describes this scenario after the transition to the business delegate.
Figure 7.15 Accessing Business Services
The command object now has a handle to the AccountDetails object, which it stores before returning control to the controller. The Controller dispatches to the appropriate view, called AccountView.jsp. The view then grabs a combination of raw data and formatted data from the AccountDetails helper and the TableFormatter helper, respectively. The TableFormatter helper is implemented as a custom tag that cycles through the raw data and formats it into an HTML table for display. As stated, this conversion requires no scriptlet code in the view, which would be necessary to perform the same functionality with a JavaBean helper.
Additionally, the Account object or the AccountDetails helper could provide convenient methods to adapt the raw data in other ways. While such methods would not introduce HTML markup into the data, they might provide different combinations of data. An example is to return the full name of the user in various formats, such as "Lastname, Firstname" or "Firstname Lastname", and so forth.
The completed view is then displayed to the user.
Transformer Helper Strategy
The helper is implemented as an eXtensible Stylesheet Language Transformer. This is particularly useful with models that exist as structured markup, such as eXtensible Markup Language (XML), either natively within legacy systems or via some form of conversion. Using this strategy can help to enforce the separation of the model from the view, since much of the view markup must be factored into a separate stylesheet.
Figure 7.16 describes a potential implementation of this strategy.
Figure 7.16 Sequence diagram for Transformer
Helper Strategy
The controller handles the request and invokes a Command object, implemented as a JavaBean helper. The Command object initiates the retrieval of Account data. The Account object invokes the business service, which returns the data in the form of a Transfer Object (see "Transfer Object" on page 261), implemented as a JavaBean.
Content retrieval is complete and control is dispatched to the AccountView, which uses its custom tag transformer to manipulate the model state. The transformer relies on a stylesheet, which describes how to transform the model, typically describing how to format it with markup for display to the client. The stylesheet is usually retrieved as a static file, though it may also be dynamically generated.
An example of how the custom tag helper might look in AccountView follows:
<xsl:transform model="accounthelper"
stylesheet="/transform/styles/basicaccount.xsl"/>
The integration of eXtensible Stylesheets and XML with JSP is evolving, as tag libraries in this area continue to mature. For now, it is a less preferred strategy, given the immature state of the supporting libraries and the additional sophisticated skills necessary to generate and maintain the stylesheets.
Consequences
Additionally, business logic that is factored out of JSPs and into JavaBeans and custom tags is reused, reducing duplication and easing maintenance.
Related Patterns
Contact Us© 2001-2002 Sun Microsystems, Inc. All Rights Reserved.