We take an introductory look at how these collaborating classes - used to solve certain, generic design problems - represent a treasure chest for Java developers.
Everybody knows what a pattern is. At least, everybody thinks so. Talking about a common-sense term, used in a specific, technical meaning, comes with both malediction and blessing. The upside is that novices don't get scared away as easy as with more "techy" names like "LL(k) grammar." Simple names suggest simple matters. The downside is that this might not be true as in our case.
Patterns are about the same principles we have successfully used in various engineering disciplines for a long time.
Let's narrow our topic. This article is an introduction to "software design patterns" (subsequently just called "patterns"). Doesn't this sound like an obsolete task, five years after the famous "Gang of Four" book [GoF95] was published? No, there are still many misconceptions around.
Some people think of design patterns as a panacea in the never-ending quest for a rational software design. Some others regard patterns just as another buzzword in the context of object oriented programming. But actually, patterns are about the same principles we have successfully used in various engineering disciplines for a long time:
- solve a certain problem by means of a "good" solution
- abstract both the problem and the solution
- apply this abstract solution to analogous problems.
Don't worry, we won't get lost in philosophical considerations. First, we define the term, then we start to argue about it.
What is a design pattern?
A design pattern is a named set of collaborating classes, used to solve a certain, generic design problem. Too simple? Yes, of course. For those who like to think in terms of pseudo code, here comes a more detailed version:
The values of the emphasized "variables" constitute a certain design pattern. This gives us the following definition:
An (abbreviated) example might look like this:
|Name ||Bridge |
|Problem ||need to access several alternative class hierarchies without changing client code |
|Intent ||separate (visible) abstraction from (hidden) implementation |
|Solution || |
|Example ||Java AWT |
|Analysis ||very different ConcreteImplementor classes might lead to fat Implementor interfaces, excessive dynamic casts, or duplicated, differently typed reference fields within the Abstraction hierarchy |
This is just one out of the 23 "standard patterns" described in [GoF95], including names like Abstract Factory
, to mention just a few of them.
But it doesn't stop with the standard patterns. There are numerous others around, filling whole Web sites, being in a constant process of refinement and consolidation.
You see, there's treasure out there. Let's see how we can dig it up.
How to use design patterns
This actually comes with two questions:
- What can we expect from using design patterns?
- Once we are convinced about their value, how do we find the "right" patterns for our problems?
The obvious answer for (1) is that we don't have to re-invent the wheel if we choose from already known solutions. This means deliberately using patterns can be a great time saver, and we know about implications of our design decisions, up-front (because of the pattern analysis). Less surprise, less hassle.
But there is more to it. Using (standard) design patterns significantly eases communication between developers. To carry on with our pattern example, the general design of the Java AWT (Abstract Windowing Toolkit) can be described as:
a Bridge pattern (Components being the Abstraction, Peers being the Implementation), with the ConcreteImplementations being created by a AbstractFactory (the Toolkit class)
One short sentence, that's (almost) all. Compare this to all the diagrams used to depict AWT classes and their relations. Without knowing about the mentioned patterns, we would have to explain at least six classes with 20 methods and fields. That makes a picture which probably takes about 30K to store, compared with the 180 bytes of description above (granted, not a fair comparison, but you get the point).
Are there typical problem domains that qualify for pattern usage? Yes, for example GUI (graphical user interface) frameworks and multi-threaded programming. Both areas have their inherent complexities and benefit from a careful analysis of design constructs. For GUIs, this is mainly about re-usability and manageable class hierarchies. With respect to threads, we primarily have to care for correct, safe interaction protocols.
There is a whole pattern community with prominent protagonists, conferences, mailing lists, and abundant literature.
When it comes to using patterns, we should not expect pre-built, ready-to-use class libraries. Design patterns are much more about class/type roles, than about concrete, re-usable classes. Somehow, patterns are for the design process what classes are for implementation. Classes group related state and behavior, to form a generic, dedicated implementation unit. Patterns group a set of classes with their collaboration protocols, to form an identifiable design unit. Classes organize fields and methods, patterns organize classes and collaborations.
Let us now turn to the second question: How do we find the right patterns for our problems?
Obviously, there are countless specific design problems, and we should expect more benefits from bigger pattern collections. As a consequence, many people have asked for additional pattern publications. But the relatively small number of patterns, described in books like [GoF95] and [BMR+96], is indeed intentional. Their authors have spent a lot of time to distill the more general, more orthogonal patterns out of many potential candidates. We definitely don't want pattern inflation.
Even with a limited number of published patterns, we have to think about how to organize them. There have been several attempts to organize pattern catalogs. The most prominent one ([GoF95]) is based on:
- purpose (what a pattern does): creational, structural and behavioral
- scope: does it organize objects or classes
Another two-dimensional scheme can be found in [BMR+96], putting more emphasis on problem categories (interactive systems, adaptive systems, access control, etc.), and development phases (architectural patterns, design patterns, idioms).
There are more-complex lookup methods around. In fact, we even have patterns dealing with this process, but these so called "pattern languages" are beyond the scope of our introduction.
Having said so many good things about patterns, we should conclude with some healthy skepticism. Sophisticated structure is not good "per se," some programmers tend to get obsessed by understood complexity. As a result, they forget about other design goals (small source base, efficiency, footprint).
For those of you who are familiar with the Java AWT, let us revisit its bridge pattern one more time. With a careful design of a generic native method interface in the portable abstraction, all the platform-specific functionality can be shifted over to the native library, eliminating the need for the whole peer hierarchy and its associated abstract factory. By not applying the bridge pattern, we get fewer classes and objects, without being less portable or extensible (a big win for embedded systems).
But don't get confused, this is not against using patterns, it is just about using them in an appropriate way. Of course, patterns do not automatically guarantee a good design.
Where to learn more
Patterns in software design are by no means a secret, unknown science. There is a whole pattern community with prominent protagonists, conferences (such as PLoP), mailing lists, and abundant literature.
To learn more, your first stop should be at the Patterns Home Page.
The following books are the language neutral "must reads":
[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].
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.
Parts of this article were taken from a "Using Patterns in Java" session of the author.
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.