Patterns and Java - A Matter of Good Taste

Wednesday Nov 17th 1999 by Peter C. Mehlitz
Share:

We conclude our overview with a quick tour through the whole Java system, looking at features that are relevant for pattern implementation.

Let's face it, Java has not been the primary language of the patterns community, when it took shape. This honor goes to C++ and Smalltalk, which isn't a surprise. The adoption of both languages for early pattern definitions was a consequence of two facts:

  1. Patterns are not constructed, they come to life because of reflection on existing problems and solutions. Both languages simply were the prevalent object-oriented languages in 1995.
  2. Patterns are a natural way to handle the increased design complexity in object-oriented systems, where we have to cope with the additional "design dimension" of class inheritance. C++ was in need of patterns because of its inherent language complexity (and lack of runtime support). Smalltalk is a simple but very dynamic language, where it is easy to get lost at runtime.

But this article is not about design patterns in general, or other programming languages than Java (we assume some knowledge about both topics). Despite all the history mentioned above, the tables have turned. Today, there is probably no better match than Java when it comes to using patterns.


The overall Java system design already constitutes an "architectural pattern."


Why is that? Java is indeed the first mainstream programming system that was influenced by design patterns from its very beginning. And it's a real "programming system," not just a "language." This was the first lesson, Java's designers learned from its predecessors — stop at the language definition, and you aim short. The runtime environment and standard libraries have to be included. This leads to a direct consequence: if the language is suitable for implementing patterns, these should show up in the libraries — and so they did.

We don't walk through implementations of the standard patterns in this article. Given their number, this would be pretty lengthy and boring. Instead, we take a quick tour through the whole Java system, looking at features which are relevant for pattern implementation, and spotting pattern examples as we go.

From ground-zero ...

The overall Java system design already constitutes what [BMR+96] calls an "architectural pattern." The approach to separate binary-compatible client code from platform-specific system code by means of a Virtual Machine, is an example of the general (high-level) LAYER pattern.

Looking at pattern-relevant language constructs, we mainly talk about idioms, not patterns themselves, i.e., ways to use the language in order to build patterns. Don't mix these rules of thumb, like "use interfaces instead of abstract classes," with real design patterns.

Interfaces are the most important language feature with respect to pattern implementation. They are far more than just a compensation for "missing" multiple inheritance. Many abstract parent classes of the standard patterns described in [GoF95] are actually just types (protocol definitions), i.e., are used to ensure availability of certain methods (e.g., STATE, VISITOR, COMMAND, OBSERVER, ITERATOR, etc.).

Without aggregates (fields), there is indeed no reason to associate a certain implementation (class) with these entities. It's hard to imagine a more straight forward construct than a Java interface to define this (e.g., compared with pure virtual bases in C++).

Classes are really simple in Java. The single inheritance prevents us from doing CLASS ADAPTERS (or other mix-in patterns). But it also enables us to call super methods, i.e., to distribute processing within a whole inheritance tree (enabling some hard-wired CHAIN OF RESPONSIBILITY patterns). The missing multiple inheritance is usually just an issue if we have to state-extend multiple classes in an existing class hierarchy, without changing inheritance relations.

In case a pattern really requires mix-ins, we have to rearrange it to use delegation (e.g., OBJECTADAPTER).

Inner Classes are Java's way of dealing with (dynamic) closures. They don't have the full power of lambda expressions or Smalltalk blocks (aka anonymous methods), since they can only access final local fields and parameters from their defining context (which makes them easier to implement and less prone to memory leaks). But they serve the same purpose — turning funtions into objects (so that they can be stored, passed around, deferred executed, etc.). This is a very important aspect for patterns like STATE, giving inner classes an outstanding role among language-specific pattern features.

Reflection is the way to cross the magic boundary between names and life objects. Pattern-related reflection comes in two different levels:

  • the simple forName() and newInstance() interface in java.lang.Class, to lookup and instantiate objects
  • the more complex API of java.lang.reflect, to access Method and Field objects

The first one is very suitable to specify concrete classes in creational patterns like ABSTRACT FACTORY. Reflection enables us to do that in a configurable way, without hard-coding class names:


AbstractFactory factory;
..
try {
  Class cls = Class.forName(property.get(``factoryCls''));
  factory = cls.newInstance();
} catch ( Exception x ) {/* not found,illegal access.. */}

The method and field access can be used for introspection in PROTOTYPE based patterns, when there is no protocol to access objects by means of a common interface or parent class (e.g., Beans)

Cloning is another feature for exemplar based patterns, where we construct objects out of existing ones. It is available via the generic clone() method, which can be seen as some kind of a COMPOSITE related pattern (treating part-whole relationsships uniformly). Since many objects (e.g. Windows) depend on some external (system) state, it is mandatory that we have per-object access to choose between shallow (Object.clone()) and deep copy.

The Cloneable interface, which is used to enable cloning on a per-class base, represents another pattern-relevant Java idiom: using marker interfaces (without methods) to tag classes.




Serialization is the measure of choice to make objects persistent, or to exchange objects between different processes (e.g., over a network). It is the backbone Java mechanism to implement patterns like PROTOTYPE (which requires some kind of a persistent object repository), or PROXY applications (like RMI, where we need to shuffle objects between the server and the client in a architecture-neutral way).


Java promotes patterns even outside its own domain.


Many pattern definitions completely ignore concurrency, but Java is meant to be used threaded. Concurrency constructs therefore should integrate seamlessly. They should require as few additional objects and protocols as possible, so that we don't end up with threaded and non-threaded variants of the same patterns.

Synchronized methods and blocks (as the visible part of object monitors) are a handy construct to ensure exclusiveness. In fact, it is so handy that people often think it's sufficient to turn all methods of a class into synchronized ones, in order to ensure thread safety. Well, it might be "safe," but sometimes in a very deadlocked way. The idiom of choice is to synchronize only local operations, but then we really can get along with it for threaded versions of patterns like SINGLETON or OBSERVER (leave alone registry modifications during notification).

The Wait and Notify mechanism has to be used if patterns need inter-object synchronization, or maybe even high-level synchronization objects, like semaphores.

Final field and parameter attributes seem to be just a minor aspect, but they actually can have an impact on patterns dealing with concurrency: since they can't change, access to final values doesn't have to be synchronized.

To see some of the above features and idioms in action, look at this:

Listing 1.  An extended example of the Future pattern from [Lea99].

Up to the libraries

With all the supporting mechanisms mentioned above, it is no surprise that we find patterns in almost all Java class libraries. Here are some examples of the kernel API:

  • the combination of the (system domain) java.lang.Thread.start() and the (application domain) Runnable.run() is a STRATEGY example.
  • java.io.FilterInputStream is a DECORATOR for InputStreams
  • java.net.URLStreamHandlerFactory is a ABSTRACT FACTORY
  • java.util.Enumeration, java.util.Iterator and its implementors are ITERATOR examples
  • java.util.Observable and java.util.Observer are a OBSERVER implementation.

RMI, of course, utilizes a PROXY pattern (among others). Due to the nature of GUIs (graphical user interfaces), most patterns can be found in the AWT (Abstract Windowing Toolkit) and its related libraries. This is only a brief list of the most obvious patterns:

  • java.awt.Toolkit is an ABSTRACT FACTORY for peer objects, it's instance is a SINGLETON
  • java.awt.Component.getGraphics() and Toolkit.getImage() are FACTORY METHODS
  • java.awt.Container and java.awt.Component are a COMPOSITE example
  • java.awt.LayoutManager is a STRATEGY implementation for geometry handling
  • the old java.awt.Event-based event handling is a CHAIN OF RESPONSIBILITY variant
  • the new java.awt.AWTEvent, the java.awt.event.EventListener interface, and the java.awt.AWTEventMulticaster form a TYPEDMESSAGE pattern (a type-safe OBSERVER variant).

Of course, pattern examples aren't restricted to the standard libraries. You can find them in many of the more-complex Java applications and third-party libraries, which indicates that most Java programmers got the message from Java's inventors.

In some sense, we could even think of an applet as a COMMAND object, with the browser acting as an Invoker. This is certainly beyond the scope of the original design pattern, but it follows the same principle and shows us that Java promotes patterns even outside its own domain.

Need advice

There is not yet "the" standard book about patterns and Java, and no Java-dedicated pattern repository. However, a lot of Java-related information can be found at these sites:

The following books provide a whole wealth of knowledge about patterns in general, specialized patterns, and Java idioms:


[GoF95] E. Gamma, R. Helm, R. Johnson, and J. Vlissides
Design Patterns — Elements of Reusable Object-Oriented Software,
Addison-Wesley, Reading, MA, 1995

This is the "dragon book" of design patterns. Almost everyone refers to it, so you should read it first.

[BMR+96] F. Buschmann, H. Rohnert, and R. Meunier
Pattern Oriented Software Architecture — A System of Patterns
Wiley, Chichester, 1996

This book examines patterns in a more-general software engineering context. It puts more emphasis on pattern classification than [GoF95].

[V98] J. Vlissides
Pattern Hatching — Design Patterns Applied
Addison-Wesley, Reading, MA, 1998

Having read the first two books, this is a very interesting reflection on how to identify, refine, and use design patterns.

[Lea99] D. Lea
Concurrent Programming in Java: Design Principles and Patterns, Second Edition
Addison-Wesley, Reading, MA, 1999

[WB99] N. Warren and P. Bishop
Java In Practice: Design Styles and Idioms for Effective Java
Addison-Wesley, Reading, MA, 1999

[La99] J. Langr
Essential Java Style: Patterns for Implementation
Prentice Hall, New York, 1999

[Gr98] M. Grand
Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, Volume 1
John Wiley & Sons, New York, 1998
Patterns in Java, Volume 2
John Wiley & Sons, New York, 1999


A short intro for design patterns can be found in part one of this article

About the author

Peter Mehlitz is co-founder of Transvirtual Technologies. He designed and implemented the AWT libraries of Kaffe. Prior to that, he worked as a technical lead for BISS GmbH, developing systems like the BSA C++-framework, the CThrough development environment, and the BISS-AWT Java libraries. Peter has about 20 years of experience in software development, using about a dozen different programming languages, with a strong focus on design issues and large, object-oriented frameworks (written in C++, Smalltalk, and Java). He holds an M.S. in aerospace engineering from the university of the German armed forces, Munich.




  Listing 1
The Future pattern

 

/*
 * This example is based on pg. 333 of
 *   Doug Lea, "Concurrent Programming in Java -- Design Principles and Patterns"
 *
 * It demonstrates the use of Futures (concurrent Proxy with join synchronization
 * via access methods) in the context of a Image rendering process.
 *
 * The rendering process itself utilizes an Abstract Factory and a
 * configurable Singleton to choose from various possible threading strategies
 * (strictly sync, thread pools, per-render thread etc.).
 * 
 * The example demonstrates the use of interfaces, reflection, inner classes
 * and synchronization for design pattern implementation
 */

// this unifies "real" Pics and Pic - futures
interface Pic {
  Image getImage();
}

// abstracts away from the render process
interface Renderer {
  Pic render( URL url );
} 

// abstracts away from raw data format
interface RawRenderer {
  Pic render ( InputStream s );
}

// utilize a configurable singleton pattern to decide upon render stratgies, use
// RendererFactory as singleton anchor and base for concrete factories
// (if it doesn't act as a backup factory, RendererFactory should be abstract)
//------------------------------------------------------------------------------
abstract class RendererFactory {
  static RendererFactory singleton;

  public static synchronized RendererFactory getFactory() {
    if ( singleton == null ){
      try {
        Class cls = Class.forName( System.getProperty( "RenderFactory"));
        singleton = cls.newInstance();
      }
      catch ( Exception x ) { /* report, choose backup factory */ }
    }
    return singleton;
  }

  //.. backup factory impl might go here (if not abstract), otherwise:
  public Renderer getRenderer( URL src ); // enforce overloading
  public RawRenderer getRawRenderer ( InputStream s );
}

// concrete renderer factory
//------------------------------------------------------------------------------
public class AdaptiveRendererFactory extends RendererFactory {
  // create Renderer according to URL protocol
  public Renderer getRenderer ( URL src ){
    String protocol = src.getProtocol();
    if ( "http".equals( src.getProtocol() ) ){
      // we don't know how long this will take, turn it async
      return new AsyncRenderer( src, this);
    }
    //..
  }

  // create concrete RawRenderer for png, gif, jpeg..
  public RawRenderer getRawRenderer(InputStream s){..}
}

// concrete renderer, which uses futures to enable async rendering
//------------------------------------------------------------------------------
public class AsyncRenderer implements Renderer {
  RendererFactory factory;// used as a strategy object for getting a RawRenderer
  final URL src;          // deferred final (because of run) -> set in ctors

  public AsyncRenderer ( URL src, RendererFactory factory ){
    this.src = src;
    this.factory = factory;
  }

  // inner class because setPic should not be accessible outside
  // AsyncRenderer object
  class FuturePic implements Pic {
    Pic pic = null;  // this will hold out resolved future

    // block until future got resolved
    public synchronized Image getImage() {
      while ( pic == null ){
        try { wait(); }
        catch ( InterruptedException x ) { return null; }
      }
      return pic.getImage();
    }

    // this is called to resolve our future, unblock clients
    synchronized void setPic ( Pic pic ){
      this.pic = pic;
      notifyAll();
    }
  }

  public Pic render () {
    // 'final' because it is used in inner class
    final FuturePic future = new FuturePic();

    Runnable r = new Runnable() {
      // this is our primitive operation called from start()
      public void run() {
        try {
          InputStream s = src.openStream();
          RawRenderer br = factory.getRawRenderer( s);
          future.setPic( br.render());  // resolve future
          s.close();
        }
        catch ( Exception x ) { throw new RenderException(..); }
      }
    }

    new Thread( r).start(); // this is the template method calling run
    return future;          // return (probably unresolved) future right away
  }
}


  // client code
  ...
  Renderer r = RendererFactory.getFactory().getRenderer( url);
  try {
    Pic pic = r.render();  // just a future, but rendering has been kicked off

    /* do useful stuff while rendering is on the way */

    Image im = pic.getImage() /* this will block until future got resolved */
    if ( im != null )
      processImage( im);
  }
  catch ( RenderException x ) { /* report error */ }
  ...


Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved