“Design patterns help you learn from others' successes instead of your
own failures[2].”
Probably the most important step forward in object-oriented design is the “design patterns”
null
movement, chronicled in Design Patterns (ibid)[3]. That book shows 23 different solutions to
particular classes of problems. In this book, the basic concepts of design patterns will be
introduced along with examples. This should whet your appetite to read Design Patterns by
Gamma, et. al., a source of what has now become an essential, almost mandatory, vocabulary
for OOP programmers.
The latter part of this book contains an example of the design evolution process, starting with
an initial solution and moving through the logic and process of evolving the solution to more
appropriate designs. The program shown (a trash sorting simulation) has evolved over time,
and you can look at that evolution as a prototype for the way your own design can start as an
adequate solution to a particular problem and evolve into a flexible approach to a class of
problems.
What is a pattern?
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 basic concept of a pattern can also be seen as the basic concept of program design: adding
a layer of abstraction. Whenever you abstract something you’re isolating particular details,
and one of the most compelling motivations behind this is to separate things that change
from things that stay the same.
null
Another way to put this is that once you find some part of
your program that’s likely to change for one reason or another, you’ll want to keep those
changes from propagating other changes throughout your code. Not only does this make the
code much cheaper to maintain, but it also turns out that it is usually simpler to understand
(which results in lowered costs).
Often, the most difficult part of developing an elegant and cheap-to-maintain design is in
discovering what I call “the vector of change.” (Here, “vector” refers to the maximum gradient
and not a container class.)
This means finding the most important thing that changes in your
system, or put another way, discovering where your greatest cost is. Once you discover the
vector of change, you have the focal point around which to structure your design.
So the goal of design patterns is to isolate changes in your code. If you look at it this way,
you’ve been seeing some design patterns already in this book. For example, inheritance can be
thought of as a design pattern (albeit one implemented by the compiler). It allows you to
express differences in behavior (that’s the thing that changes) in objects that all have the
same interface (that’s what stays the same). Composition can also be considered a pattern,
since it allows you to change—dynamically or statically—the objects that implement your
class, and thus the way that class works.
You’ve also already seen another pattern that appears in Design Patterns: the iterator (Java
1.0 and 1.1 capriciously calls it the Enumeration; Java 2 containers use “iterator”). This
hides the particular implementation of the container as you’re stepping through and selecting
the elements one by one. The iterator allows you to write generic code that performs an
operation on all of the elements in a sequence without regard to the way that sequence is
built. Thus your generic code can be used with any container that can produce an iterator.
Pattern taxonomy
One of the events that’s occurred with the rise of design patterns is what could be thought of
as the “pollution” of the term – people have begun to use the term to mean just about
anything synonymous with “good.” After some pondering, I’ve come up with a sort of
hierarchy describing a succession of different types of categories:
1. Idiom: how we write code in a particular language to do this particular type of thing.
This could be something as common as the way that you code the process of stepping
through an array in C (and not running off the end).
2. Specific Design: the solution that we came up with to solve this particular problem.
This might be a clever design, but it makes no attempt to be general.
3. Standard Design: a way to solve this kind of problem. A design that has become
more general, typically through reuse.
4. Design Pattern: how to solve an entire class of similar problem. This usually only
appears after applying a standard design a number of times, and then seeing a
common pattern throughout these applications.
I feel this helps put things in perspective, and to show where something might fit. However, it
doesn’t say that one is better than another. It doesn’t make sense to try to take every problem
solution and generalize it to a design pattern – it’s not a good use of your time, and you can’t
force the discovery of patterns that way; they tend to be subtle and appear over time.
One could also argue for the inclusion of Analysis Pattern and Architectural Pattern in this
taxonomy.
Design principles
(Update from slides to here)
When I put out a call for ideas in my newsletter[4], a number of suggestions came back which
turned out to be very useful, but different than the above classification, and I realized that a
list of design principles is at least as important as design structures, but for a different reason:
these allow you to ask questions about your proposed design, to apply tests for quality.
• Principle of least astonishment (don’t be astonishing).
• Make common things easy, and rare things possible
• Consistency. One thing has become very clear to me, especially because of Python:
the more random rules you pile onto the programmer, rules that have nothing to do
with solving the problem at hand, the slower the programmer can produce. And this
does not appear to be a linear factor, but an exponential one.
• Law of Demeter: a.k.a. “Don’t talk to strangers.” An object should only reference
itself, its attributes, and the arguments of its methods.
• Subtraction: a design is finished when you cannot take anything else away.
• Simplicity before generality[5]. (A variation of Occam’s Razor, which says “the
simplest solution is the best”). A common problem we find in frameworks is that they
are designed to be general purpose without reference to actual systems. This leads to a
dizzying array of options that are often unused, misused or just not useful. However,
most developers work on specific systems, and the quest for generality does not
always serve them well. The best route to generality is through understanding welldefined
specific examples. So, this principle acts as the tie breaker between otherwise
equally viable design alternatives. Of course, it is entirely possible that the simpler
solution is the more general one.
• Reflexivity (my suggested term). One abstraction per class, one class per
abstraction. Might also be called Isomorphism.
• Independence or Orthogonality. Express independent ideas independently. This
complements Separation, Encapsulation and Variation, and is part of the Low-
Coupling-High-Cohesion message.
• Once and once only: Avoid duplication of logic and structure where the duplication
is not accidental, ie where both pieces of code express the same intent for the same
reason.
In the process of brainstorming this idea, I hope to come up with a small handful of
fundamental ideas that can be held in your head while you analyze a problem. However, other
ideas that come from this list may end up being useful as a checklist while walking through
and analyzing your design.
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 book 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 book contains examples of the Observer and the Visitor patterns.
The Design Patterns book has a section on each of its 23 patterns along with one or more
examples for each, typically in C++ (rather restricted C++, at that) but sometimes in
Smalltalk. (You’ll find that this doesn’t matter too much since you can easily translate the
concepts from either language into Java.) This book will revisit many of the patterns shown in
Design Patterns but with a Java orientation, since the language changes the expression and
understanding of the patterns. However, the GoF examples will not be repeated here, since I
believe that it’s possible to produce more illuminating examples given some effort. The goal is
to provide you with a decent feel for what patterns are about and why they are so important.
After years of looking at these things, it began to occur to me that the patterns themselves use
basic principles of organization, other than (and more fundamental than) those described in
Design Patterns. These principles are based on the structure of the implementations, which is
where I have seen great similarities between patterns (more than those expressed in Design
Patterns). Although we generally try to avoid implementation in favor of interface, for awhile
I thought that it was easier to understand the patterns in terms of these structural principles,
and tried reorganizing the book around the patterns based on their structure instead of the
categories presented in Design Patterns.
However, a later insight made me realize that it’s more useful to organize the patterns in
terms of the problems they solve. I believe this is a subtle but important distinction from the
way Metsker organizes the patterns by intent in Design Patterns Java Workshop (Addison-
Wesley 2002), because I hope that you will then be able to recognize your problem and search
for a solution, if the patterns are organized this way.
In the process of doing all this “book refactoring” I realized that if I changed it once, I would
probably change it again (there’s definitely a design maxim in there), so I removed all
references to chapter numbers in order to facilitate this change (the little-known “numberless
chapter” pattern ☺).
The development challenge
Issues of development, the UML process, Extreme Programming.
Is evaluation valuable? The Capability Immaturity Model:
Wiki Page: http://c2.com/cgi-bin/wiki?CapabilityImMaturityModel
Article: http://www.embedded.com/98/9807br.htm
Pair programming research:
http://collaboration.csc.ncsu.edu/laurie/
Unit testing
In an earlier version of this book I decided that unit testing was essential (for all of my books)
and that JUnit was too verbose and clunky to consider. At that time I wrote my own unit
testing framework using Java reflection to simplify the syntax necessary to achieve unit
testing. For the third edition of Thinking in Java, we developed another unit testing
framework for that book which would test the output of examples.
In the meantime, JUnit has changed to add a syntax remarkably similar to the one that I used
in an earlier version of this book. I don’t know how much influence I may have had on that
change, but I’m simply happy that it has happened, because I no longer feel the need to
support my own system (which you can still find
recommend the defacto standard.
I have introduced and described the style of JUnit coding that I consider a “best
practice” (primarily because of simplicity), in Thinking in Java, 3rd edition, chapter 15. That
section provides an adequate introduction to any of the unit testing you will see associated
with this book (however, the unit testing code will not normally be included in the text of this
book). When you download the code for this book, you will find (4/9/2003: Eventually, not
yet) unit tests along with the code examples whenever possible
Location of test code
Public: in test subdirectory; different package (don’t include in jar).
Package access: same package, subdirectory path underneath library code (don’t include in
jar)
Private access: (white box testing). Nested class, strip out, or Junit addons.
null
Simplifying Idioms
Before getting into more complex techniques, it’s helpful to look at some basic ways to keep
code simple and straightforward.
Messenger
The most trivial of these is the messenger, which simply packages information into an object
to be passed around, instead of passing all the pieces around separately. Note that without the
messenger, the code for translate() would be much more confusing to read:
//: simplifying:MessengerDemo.java
package simplifying;
import junit.framework.*;
class Point { // A messenger
public int x, y, z; // Since it's just a carrier
public Point(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public Point(Point p) { // Copy-constructor
this.x = p.x;
this.y = p.y;
this.z = p.z;
}
public String toString() {
return "x: " + x + " y: " + y + " z: " + z;
}
}
class Vector {
public int magnitude, direction;
public Vector(int magnitude, int direction) {
this.magnitude = magnitude;
this.direction = direction;
}
}
class Space {
public static Point translate(Point p, Vector v) {
p = new Point(p); // Don't modify the original
// Perform calculation using v. Dummy calculation:
p.x = p.x + 1;
p.y = p.y + 1;
p.z = p.z + 1;
return p;
}
}
public class MessengerDemo extends TestCase {
public void test() {
Point p1 = new Point(1, 2, 3);
Point p2 = Space.translate(p1, new Vector(11, 47));
String result = "p1: " + p1 + " p2: " + p2;
System.out.println(result);
assertEquals(result,
"p1: x: 1 y: 2 z: 3 p2: x: 2 y: 3 z: 4");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(MessengerDemo.class);
}
} ///:~
Since the goal of a messenger is only to carry data, that data is made public for easy access.
However, you may also have reasons to make the fields private.
Collecting Parameter
Messenger’s big brother is the collecting parameter, whose job is to capture information from
the method to which it is passed. Generally, this is used when the collecting parameter is
passed to multiple methods, so it’s like a bee collecting pollen.
A container makes an especially useful collecting parameter, since it is already set up to
dynamically add objects:
//: simplifying:CollectingParameterDemo.java
package simplifying;
import java.util.*;
import junit.framework.*;
class CollectingParameter extends ArrayList {}
class Filler {
public void f(CollectingParameter cp) {
cp.add("accumulating");
}
public void g(CollectingParameter cp) {
cp.add("items");
}
public void h(CollectingParameter cp) {
cp.add("as we go");
}
}
public class CollectingParameterDemo extends TestCase {
public void test() {
Filler filler = new Filler();
CollectingParameter cp = new CollectingParameter();
filler.f(cp);
filler.g(cp);
filler.h(cp);
String result = "" + cp;
System.out.println(cp);
assertEquals(result,"[accumulating, items, as we go]");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(
CollectingParameterDemo.class);
}
} ///:~
The collecting parameter must have some way to set or insert values. Note that by this
definition, a messenger could be used as a collecting parameter. The key is that a collecting
parameter is passed about and modified by the methods it is passed to.
Object quantity
The two patterns described here are solely used to control the quantity of
objects.
Singleton could actually be thought of as a special case of Object Pool, but the applications of
the Object Pool tend to be uniqe enough from Singleton that it’s worth treating the two
separately.