November 28, 2020
Hot Topics:

Patterns and Java - A Matter of Good Taste

  • By Peter C. Mehlitz
  • Send Email »
  • More Articles »

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.

Page 1 of 3

This article was originally published on November 17, 1999

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date