The pattern concept

Bruce Eckel’s Thinking in Java Contents | Prev | Next

Initially,
you can think of a pattern as an especially clever and insightful way of
solving a particular class of problems. That is, it looks like a lot of people
have worked out all the angles of a problem and have come up with the most
general, flexible solution for it. The problem could be one you have seen and
solved before, but your solution probably didn’t have the kind of
completeness you’ll see embodied in a pattern.

Although
they’re called “design patterns,” they really aren’t
tied to the realm of design. A pattern seems to stand apart from the
traditional way of thinking about analysis, design, and implementation.
Instead, a pattern embodies a complete idea within a program, and thus it can
sometimes appear at the analysis phase or high-level design phase. This is
interesting because a pattern has a direct implementation in code and so you
might not expect it to show up before low-level design or implementation (and
in fact you might not realize that you need a particular pattern until you get
to those phases).

The
singleton

//: SingletonPattern.java
// The Singleton design pattern: you can
// never instantiate more than one.
package c16;
 
// Since this isn't inherited from a Cloneable
// base class and cloneability isn't added,
// making it final prevents cloneability from
// being added in any derived classes:
final class Singleton {
  private static Singleton s = new Singleton(47);
  private int i;
  private Singleton(int x) { i = x; }
  public static Singleton getHandle() {
    return s;
  }
  public int getValue() { return i; }
  public void setValue(int x) { i = x; }
}
 
public class SingletonPattern {
  public static void main(String[] args) {
    Singleton s = Singleton.getHandle();
    System.out.println(s.getValue());
    Singleton s2 = Singleton.getHandle();
    s2.setValue(9);
    System.out.println(s.getValue());
    try {
      // Can't do this: compile-time error.
      // Singleton s3 = (Singleton)s2.clone();
    } catch(Exception e) {}
  }
} ///:~ 

The
key to creating a singleton is to prevent the client programmer from having any
way to create an object except the ways you provide. You must make all constructors
private,
and you must

create
at least one constructor to prevent the compiler from
synthesizing
a default constructor for you (which it will create as “friendly”).

At
this point, you decide how you’re going to create your object. Here,
it’s created statically, but you can also wait until the client
programmer asks for one and create it on demand. In any case, the object should
be stored privately. You provide access through public methods. Here,
getHandle( )
produces the handle to the
Singleton
object. The rest of the interface (
getValue( )
and
setValue( ))
is the regular class interface.

Java
also allows the creation of objects through cloning. In this example, making
the class
final
prevents cloning. Since
Singleton
is inherited directly from
Object,
the
clone( )
method remains
protected
so it cannot be used (doing so produces a compile-time error). However, if
you’re inheriting from a class hierarchy that has already overridden
clone( )
as
public
and implemented
Cloneable,
the way to prevent cloning is to override
clone( )
and throw a
CloneNotSupportedException
as described in Chapter 12. (You could also override
clone( )
and simply return
this,
but that would be deceiving since the client programmer would think they were
cloning the object, but would instead still be dealing with the original.)

Classifying
patterns

The
Design
Patterns

book discusses 23 different patterns, classified under three purposes (all of
which revolve around the particular aspect that can vary). The three purposes
are:

  1. Creational:
    how an object can be created. This often involves isolating the details of
    object creation so your code isn’t dependent on what types of objects
    there are and thus doesn’t have to be changed when you add a new type of
    object. The aforementioned
    Singleton
    is classified as a creational pattern, and later in this chapter you’ll
    see examples of
    Factory
    Method

    and
    Prototype.
  2. Structural:
    designing objects to satisfy particular project constraints. These work with
    the way objects are connected with other objects to ensure that changes in the
    system don’t require changes to those connections.
  3. Behavioral:
    objects that handle particular types of actions within a program. These
    encapsulate processes that you want to perform, such as interpreting a
    language, fulfilling a request, moving through a sequence (as in an iterator),
    or implementing an algorithm. This chapter contains examples of the
    Observer
    and the
    Visitor
    patterns.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read