Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Java design patterns 201

Paul Monday, Developer
Paul Monday is a contributing developerWorks author.

Summary:  Design patterns extend far beyond those described by the famous Gang of Four. In this tutorial, you will find out just how much. Veteran developer Paul Monday begins his discussion by exploring resources that newcomers to the study of design patterns often miss. Then he uses design patterns from these resources to implement a simple application. Finally, he switches his focus to how design patterns can help you to better understand software design and guides you through the reverse-engineering of a piece of technology, focusing on how it works from the perspective of patterns.

Date:  09 Apr 2002
Level:  Introductory PDF:  A4 and Letter (241 KB | 41 pages)Get Adobe® Reader®

Activity:  38507 views
Comments:  

Patterns at work

Patterns at work overview

In this section and the two that follow we'll uncover, evaluate, and use several design patterns to build a Web-based application. This application will allow users to order movies from a Web site. We'll use four design patterns to build the application: two business patterns, one Web presentation pattern, and one J2EE pattern. We'll start with a look at the business patterns.

Business patterns

Patterns specific to problems in vertical business applications can be hard to find. Fortunately, two books currently on the market, Patterns for e-business: A Strategy for Reuse and SanFrancisco Design Patterns: Blueprints for Business Software, focus almost entirely on business patterns. (See Resources for details on these titles.)

The business patterns each book describes vary considerably in terms of granularity. The first, Patterns for e-business, presents coarse-grained architectural patterns. These patterns are geared to helping you choose a path for structuring and architecting an e-business system. Because these patterns are architectural in nature, we won't implement any of them here. You might benefit from further study of these patterns, however, with particular attention to how the architectural patterns differ from a lower-level design pattern.

The patterns found in SanFrancisco Design Patterns: Blueprints for Business Software are lower-level design patterns, structured similarly to the GOF patterns. Two of these patterns, the Property Container pattern and the Simple Policy pattern, will be the basis of our movie-ordering application.

The Property Container pattern

Property Container is a foundational pattern. Its function is to ensure that an application, once built and deployed, can be dynamically extended.

Consider the movie-ordering application. When designing a class to represent a movie, it's unlikely that you would concern yourself with all the attributes to describe a movie. But as a conscientious designer, you would want to ensure that the movie class could be extended with additional data as needed. Some extension mechanisms require a programmer to recompile the code and redeploy the classes to be extended. Property Container, on the other hand, gives us a mechanism to dynamically extend an object with additional attributes at run time. Of course, creating the Property Container is only the beginning; the application may also require modules that explicitly take advantage of the new property once it's been added.

A variety of mechanisms exist for making the Movie class a Property Container. Ours will be a fairly simple approach: we'll create a standard interface for the Property Container, then extend or implement that interface for all interfaces and classes that we want to be able to modify at run time.

In the next several sections we'll design a Property Container interface, then extend that interface to define the Movie interface that is the basis of our movie-ordering application. We'll also work with the concrete class implementations for the Property Container and Movie interfaces.


Designing a Property Container

In the following UML class diagram, notice that we have created a very simple Property Container interface and an abstract implementation from which classes can inherit:

A UML diagram of classes for the Property Container pattern

As you can see, there are four methods to add, remove, and query properties in the Property Container. The properties are identified by a String key. In the concrete implementation we'll simply use a hashtable to store the properties as they're added to the Movie class.

We're working with a very simple Property Container design and implementation here, but the actual pattern has some critical extensions. These extensions illustrate the different ways the pattern can function in a more robust and extensive implementation. For example, if an object is in a hierarchy of run-time objects, the Property Container pattern allows for the traversal of that run-time hierarchy.

One example of a run-time hierarchy is a department contained within another department, which in turn is contained within a company. A query against a department's dynamic properties for an accounting code would make use of the hierarchical Property Container. The Property Container pattern would define the behavior for traversing the containment hierarchy for the specific property of the accounting code.


Implementing a Property Container

Our implementation of the Property Container will be very straightforward. To get started, take a look at the Property Container code below:

public abstract class PropertyContainerImpl
      implements PropertyContainer, Serializable
{

      protected Hashtable ivProperties = new Hashtable(2);

      public PropertyContainerImpl() {
      }

      /**
       * Add a property associated with a token name.
       * If the token already exists, the value will be replaced.
       * If the token does not exist, it will be added with the value.
       * @param value is an object that cannot be null
       * @param token is a key that can be used to retrieve the value
       */
      public void addPropertyBy(Object value, String token) {
          if(value==null || token==null) return;
          if(ivProperties.containsKey(token)){
              ivProperties.remove(token);
          }
          ivProperties.put(token, value);
      }

      /**
       * Retrieve a value by a particular token.
       * @param token is a key that can be used to retrieve the value
       * @return Object is the value associated with the token.  It
       *   will not be null.
       */
      public Object getPropertyBy(String token) {
          if(token==null) return null;
          return ivProperties.get(token);
      }

      /**
       * Retrieve all property keys currently in use.
       * @return String[] is an array of all valid token names.
       */
      public String[] getPropertyKeys() {
          String keys[] = null;
          synchronized(ivProperties){
              int s = ivProperties.size();
              keys = new String[s];
              Enumeration e = ivProperties.keys();
              int i = 0;
              while(e.hasMoreElements()){
                  keys[i] = (String)e.nextElement();
                  i++;
              }
          }
          return keys;
      }

      /**
       * Remove a value associated with a particular token.
       * @param token is a key associated with a value that was added
       */
      public void removeProperty(String token) {
          if(token==null) return;
          ivProperties.remove(token);
      }

}

As you can see, our Property Container uses a Hashtable to store property values. The key is determined by the person adding the property. You will typically want to standardize the keys in some way, such as the package name and class responsible for adding the property.


Implementing the Movie class

The next step is to use the Property Container interface and abstract class implementation to implement the Movie interface. We'll use classic, simple JavaBean patterns to devise the properties for our Movie interface implementation. The implementation, MovieImpl, will inherit from the abstract PropertyContainerImpl implementation in the previous panel.

public class MovieImpl extends PropertyContainerImpl
      implements Movie, Serializable
{

      private int available;

      private String description;

      private float price;

      private String rating;

      private String title;

      private String id;

      public MovieImpl() {
      }

      public int getAvailable() {
          return this.available;
      }

      public void setAvailable(int available) {
          this.available = available;
      }

      public String getDescription() {
          return this.description;
      }

      public void setDescription(String description) {
          this.description = description;
      }

      public float getPrice() {
          return this.price;
      }

      public void setPrice(float price) {
          this.price = price;
      }

      public String getRating() {
          return this.rating;
      }

      public void setRating(String rating) {
          this.rating = rating;
      }

      public String getTitle() {
          return this.title;
      }

      public void setTitle(String title) {
          this.title = title;
      }

      public String getId() {
          return id;
      }

      public void setId(String id) {
          this.id = id;
      }

}

You should find no surprises in our MovieImpl class implementation. Recall that all the behavior for the Property Container is actually contained in the PropertyContainerImpl superclass. The remainder of the code is simply JavaBean patterns, which fill in the primary attributes of the MovieImpl class.


Property Container demo

After creating several Movie objects and placing them in a vector, we may find that some of the movies have additional attributes. For example, one of the movies may not be released yet. Rather than extending the class, we can add the releasedate property using the inherited addPropertyBy method on the Movie class, as shown here:

          Movie temp = (Movie)movies.elementAt(1);
          temp.addPropertyBy(
              new GregorianCalendar(2002, 5, 22),
              "releasedate");


A test program that populates two Movie objects and adds a release date property is in the package com.stereobeacon.patterns.business.propertycontainer. To run the program, use the com.stereobeacon.patterns.business.propertycontainer.TestPropertyContainer class.


Pros and cons of the Property Container pattern

There are some disadvantages to using the Property Container pattern. With the off-the-shelf property container implementation, you lose strong typing. Also, the interface to the class is not entirely descriptive of the contents, and you'll probably have to modify a user interface to take proper advantage of the added attribute. If you're using the method of serialization to a database, the Property Container itself could cause some headaches as well.

Despite the drawbacks, the Property Container pattern is well suited for certain types of applications, especially when coupled with the ability to traverse a containment hierarchy.

We'll put the Property Container aside for now, as we concentrate on integrating another essential pattern into our application.


The Simple Policy pattern

The Simple Policy pattern is similar to the GOF's Strategy design pattern, but specialized to deal with the realities of business. The Simple Policy pattern allows us to dynamically set and enforce access policies for our application.

For example, say that an online movie store wants to enforce a policy to restrict the sale of movies to minors, based on movie ratings. The store executives haven't settled on a single policy, however, so our movie-ordering application has to be flexible enough to change policies with ease. Furthermore, movie ratings occasionally change, so the application must be able to take into account new ratings scales or criteria when an order is placed. All of this means that the rules for validating that a particular customer is old enough to order a particular movie could be considered volatile code. Rather than having to recompile new code every time the store policy or industry ratings system changes, it would be easier to isolate the volatile code and allow it to change dynamically. This is essentially what the Simple Policy pattern does.


Designing a Simple Policy

The Order class contains the volatile validation logic. This logic is extracted out into concrete classes that implement a single interface. In our case, the interface is named AgeValidationPolicy and there are two different policy implementations, as follows:

  • AgeValidationPolicyDefault follows the U.S. movie rating recommendations (for example, a PG-13 movie cannot be sold to anyone under age 13).
  • AgeValidationPolicyLoose says that anything up to and including R-rated movies can be purchased by individuals of any age.

In addition to the policy classes, we have an Order object that contains a customer and one or more movies, as shown below:

A UML diagram of classes for the Simple Policy pattern

Not shown in the diagram is a simple registry, which implements the GOF's Singleton pattern (see Resources ). To keep things simple, the registry is a concrete implementation of the Property Container pattern. When an order is created, the Order object locates the validation policy in the registry, then passes the customer information and movie order to the validation policy. The policy throws a run-time error if the customer is unable to purchase the movies due to a policy violation.

The run-time registry is convenient because it lets us change the validation policy on the fly. So, if the store executives knew that the store's sales were going to be audited by a movie watchdog group, they could run the default policy. Once the audit was complete, they could replace the default policy with the "loose" policy, thus increasing sales. Integrating the Simple Policy pattern into our application ensures that policy changes don't require recompilation, just that the new policy be registered in the run-time registry.


Implementing a Simple Policy

The interesting portion of the Order class is where the validation occurs. To demonstrate how the validation policy works, we'll look at the constructor on the concrete Order implementation:

      /** Creates a new instance of OrderImpl */
      public OrderImpl(Customer customer, Movie[] movies) {
          AgeValidationPolicy policy =
              (AgeValidationPolicy)
                  PolicyRegistry.getInstance().getPropertyBy(
                      AgeValidationPolicy.Token
                  );
          if(policy==null) policy = new AgeValidationPolicyDefault();
          policy.validatePurchasingAge(customer, movies);
          this.customer = customer;
          this.movie = movies;
      }


Upon attempting to create an instance of an order, the constructor retrieves the Singleton policy registry and gets the validation policy by a predefined token. If no validation policy is registered, the default policy is used. We then call the validatePurchasingAge method on the policy. If the policy fails, it returns a run-time exception and the constructor will also fail. Otherwise, we go ahead and set the customer and movie array.


Simple Policy demo

Once we're in the order constructor, we assume that someone is maintaining the registry with the age validation policy. The test program com.stereobeacon.patterns.business.simplepolicy.TestSimplePolicy demonstrates the run-time exchange of policies. To run the demo, we first add the default age validation to the registry. Once added, the default policy will be the one retrieved by the AgeValidationPolicy.Token (the token is simply the string "com.stereobeacon.patterns.business.simplepolicy.AgeValidationPolicy").

      public static void main(String[] args) {
          PolicyRegistry.getInstance().addPropertyBy(
              new AgeValidationPolicyDefault(), AgeValidationPolicy.Token
              );

          Movie[] movies = createMovies();
          Customer customer = new CustomerImpl();
          customer.setAge(17);
          customer.setName("Paul");

          try {
              Order o = new OrderImpl(customer, movies);
              System.out.println("Successful Order Creation");
          } catch(ValidationException exception){
              System.out.println("Could not create Order due to Validation Exception");
              System.out.println("Message is:  "+exception.getMessage());
          }

          PolicyRegistry.getInstance().addPropertyBy(
              new AgeValidationPolicyLoose(), AgeValidationPolicy.Token
              );

          try {
              Order o = new OrderImpl(customer, movies);
              System.out.println("Successful Order Creation");
          } catch(ValidationException exception){
              System.out.println("Could not create Order due to Validation Exception");
              System.out.println("Message is:  "+exception.getMessage());
          }

      }

Once the policy is added, we populate some movies and create a 17-year-old customer, Paul. Clearly, our customer should not be able to order R-rated movies. Upon creating the first order, we'll receive a validation exception. Next, we change the registry and use the loose validation policy, which allows anyone to order R-rated movies. The second order will successfully create, even with the same movies and customer. And with that, we've dynamically changed the validation policy at run time.


Notes about the Simple Policy pattern

Like the Property Container implementation, our implementation of the Simple Policy pattern is very simple, offering only a glimpse of what the pattern can do. Policies are very powerful when associated with containment hierarchies.

Take the example of a chain of movie rental stores owned by a parent company. The parent company sets a default sales policy for all branches, but each branch is also empowered to implement its own sales policy. Thus, a branch could set an individual policy that would be contained by the larger company policy at run time. The algorithm would then be set to check the store for a default movie sales policy; if one did not exist the parent company would be queried for the default policy, otherwise the store's individual policy would be used.


Patterns summary

In this section we implemented two design patterns from SanFrancisco Design Patterns. These patterns were defined for a specific type of application, in this case business applications.

Integrating the Property Container into our application allows us to add attributes, such as release date, to our Movie class at run time, making the overall movie catalog easier to update. The Simple Policy implementation isolates validation logic, letting us change our sales policy at will. We'll use the code from both of these examples in the next section, as we continue building our Web-based movie-ordering application.

See Resources to learn more about these two design patterns and others to be found in SanFrancisco Design Patterns.

3 of 10 | Previous | Next

Comments



static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=131978
TutorialTitle=Java design patterns 201
publish-date=04092002
author1-email=
author1-email-cc=