Design Patterns 15 Years Later: An Interview with Erich Gamma, Richard Helm, and Ralph Johnson

Larry O'Brien recently interviewed three of the Gang of Four about their seminal work on patterns. Larry teased the interview's readers for awhile, but he eventually asked the pressing question that most language designers ask and debate about patterns ;) Here it is:

Larry: GoF came out relatively early in the ascent of OOP as the mainstream paradigm and, for better or worse, "patterns" seem to be associated with OO approaches. You even hear functional and dynamic advocates boasting that their languages "don't need" patterns. How do you respond to that?

Erich: Just as an aside, it is also easy to forget that we wrote design patterns before there was Java or C#.

Ralph: Some of those languages don't need some of the patterns in Design Patterns because their languages provide alternative ways of solving the problems. Our patterns are for languages between C++ and Smalltalk, which includes just about everything called "object-oriented," but they certainly are not for every programming language. I don't think anybody actually says that programmers in other languages don't need patterns; they just have a different set of patterns.

Erich: Design patterns eventually emerge for any language. Design déjà-vu is language neutral. While these experiences are not always captured as patterns, they do exist. The design principles for Erlang come to mind.

Larry: Where would a person go to learn about patterns for dynamic and functional languages? Who's making good contributions?

Ralph: If by "dynamic" you mean dynamic object-oriented languages like Smalltalk, Ruby or Python, then our patterns are applicable. Functional languages require different patterns, but I don't know who is working on them.

Note: At the end of the interview, Erich says that they tried refactoring the patterns into new categories in 2005. The draft breakdown he provides (accidentally???) takes out Memento, Chain of Responsibility, Bridge, Adapter, and Observer.

As I said above these are just notes in a draft state. Doing a refactoring without test cases is always dangerous...

UPDATE: The Gang of Four have an accompanying article for the interview that they wrote as a group. See A Look Back: Why We Wrote Design Patterns: Elements of Reusable Object-Oriented Software.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Relationship between Design Patterns and Programming Paradigms

In this short paper, I explain the relationship between design patterns and programming paradigms.

Important story

The story text mentions the GoF book, and links to the design principles for Erlang text. The point that "Design patterns eventually emerge for any language. Design déjà-vu is language neutral" has not been lost on everyone not using C++, Smalltalk, Java, and Erlang: it might be timely to gather together what documentation of patterns we can find for other languages, and perhaps this thread is the place to do it.

Haskell/ML Design Patterns

Haskell has a few common "design patterns," if you want to call it that: monoids, monads, and applicative functors come to mind.

Then you have applications of open recursion: adamo recently re-linked to the short and sweet Y in Practical Programs, and Wouter Swiestra's quite lovely Data Types a la Carte employs open recursion at the type level.

Of course, I'm barely stratching the surface here. Really, I don't think there is any lack of design patterns for functional programming.

...as a quick search through

...as a quick search through the archive will show.

But the interesting point, language design-wise, I think, is the standard claim that patterns are design recipes not that cannot be captured as code. This claim necessarily depends on the expressive power of the language concerned.

Context

Classically, patterns include a description of the context in which the pattern is intended to be used, a description of the kind of problem solved by the pattern, a description of the recommended solution, and sometimes pointers to subsidiary patterns that help in completing the current pattern. Most languages today are only really good at capturing the solution part - usually as some kind of generic structure or algorithm*. I suppose that some languages may make it possible to provide pointers to subsidiary patterns by restricting the type of objects or functions that must be provided to complete the solution. But a description of context (beyond some relatively simple preconditions on functions) or the problem is usually only found in comments, not in code.

Could the context and problem be expressed in code? Perhaps. But it would probably require adding the ability to express some concepts that are quite foreign to mainstream programming languages at present. Consider the Flyweight pattern, which solves the problem of reducing memory consumption in the context of large numbers of similar objects. Are there any languages that allow the description of goals like "minimization of representation memory size", or of ideas like object "similarity"? More interestingly, assuming you could express such ideas in code, how would you use them? Christopher Alexander originally envisioned the use of a pattern language as a kind of progressive unfolding of patterns. Would programming with a language expressive enough to capture patterns become a kind of interactive selection of lower-level patterns that solve the problems and fit the context of the current pattern? Perhaps using a development environment a little like an interactive proof assistant...hmmm...

* Although even that approach isn't quite right, since a pattern in the original sense of Alexander is not supposed to have rigidly predefined points of variability.

That's the easy way out...

That's the easy way out... So let's put the issue of context aside and ask if the other components in the description of patterns are encodable in a give language. Surely the answer will differ depending on the language.

A Moving Target

So let's put the issue of context aside and ask if the other components in the description of patterns are encodable in a give language.

This question only makes sense if we assume that a "design pattern" is some kind of universal object that applies to all languages for all time.

It has always been clear to me that the GoF guys were just trying to transmit the "folklore" of their design experience with OOP to a new generation that was just learning about it.

How do you encode design experience into a PL? If a design pattern can be encoded as a feature in your language, in a sense it stops being a design pattern and becomes a design element.

But this new element might spawn new, more focused design patterns. To take a specific example, you could argue that Accumulator Passing was/is a design pattern in functional programming, which morphed into the Monad pattern. In Haskell, this latter pattern got specific support in the language, so now the folklore might have more to do with how to convert specific kinds of things into a monad. The design pattern has shrunk as parts of it have been formalized in the language.

And in the meantime perhaps new design patterns have been invented, such as a Zipper pattern.

If design patterns really are the bits you can't encode in the language directly, they are going to be a moving target as new language feature are invented and new patterns are identified.

Can't encode in the paradigm vs. can't encode in language

I think OO is more of a style and/or paradigm, and things such as Visitor to approximate robust dynamic dispatch help to define the paradigm's expressive limits and essential boilerplate.

You may come up with a macro or DSL to circumvent this, but a macro/DSL is inherently general-systems-theory paradigm-agnostic.

Can't decode the paradigm

I think OO is more of a style and/or paradigm...
help to define the paradigm's expressive limits and essential boilerplate.

One thing we have learned amply around here over the years is that "paradigm" is an inherently fuzzy notion, and by definition it's pretty hard to determine the exact limits of a fuzzy notion.

For the purposes of this post, let's assume that OOP means an inclination to express design by organizing operations and data into classes in a hierarchy.

things such as Visitor to approximate robust dynamic dispatch

If you define Visitor this way, then you support my point: choose a language that supports dynamic dispatch and the pattern goes away or changes its focus.

The irreducibly "design pattern" part of Visitor is something like this: due to my OOP inclination (and perhaps the limitations of my language), I want to organize my operations and data into a single canonical class hierarchy. The particular organization of classes and hierarchy I choose are my "design".

I choose this design because for, say, 90% of my purposes this the organization that makes sense. But I find there are 10% of cases where a different grouping of data and operations would be convenient. What do I do? The Visitor pattern suggest that I create a Visitor class that expresses the new grouping.

Now say someone is using a language that supports multiple orthogonal class hierarchies (I seem to recall hearing of such a language, and certainly AOP could be described as a form of this ). If the prevailing design inclination in that community is to ditch designs that have a single canonical class hierarchy, then the design motivation for this pattern goes up in smoke, and it ceases to be a meaningful design pattern in that community.

One way to sum this up is to say that "design pattern" is primarily a social and aesthetic phenomenon, and only indirectly a technical one.

The irreducibly "design

The irreducibly "design pattern" part of Visitor is something like this

As a slight aside, first-class messages do away with the need for the visitor pattern entirely. They are OO's equivalent of algebraic data types, as type classes are a functional language's equivalent of overloading/dispatch.

This supports the point that design patterns are dictated by the language's expressiveness.

Applicability

This supports the point that design patterns are dictated by the language's expressiveness.

Which is perhaps another way of saying that the design patterns that are applicable depend on the context in which you're working. Again going back to Alexander's original pattern language, the Place to Wait pattern is only really applicable within the context of larger patterns that require people to wait (such as Interchange, Health Center, Small Services without Red Tape, or Office Connections). Similarly, using the visitor pattern is only really going to be a useful pattern in the context of languages that don't already provide direct support for similar capabilities (thus creating a problem to which the pattern provides a solution).

But I find there are 10% of

But I find there are 10% of cases where a different grouping of data and operations would be convenient.

Then you have incorrectly agreed on the problem domain vocabulary between you and your problem domain expert. It happens, always to me. I routinely make huge design shortcuts my CEO wants to take my head off for later down the road, but they're essential to me understanding the system as a whole.

What do I do?

I am going to give you the answer the iterative, incremental design folks gave the waterfall folks in the '70s, '80s, and '90s: REFACTOR.

Some systems, however, have no problem domain experts, and in fact change so often and so arbitrarily as to seemingly have no problem domain. This is known as politics, and this realm of software is mostly confined to governments.

I am not sure what the right solution is to such problems. As you mention, AOP is one idea, where in its current state-of-the-art the composition-approach is web/tangle/weave. Similar web/tangle/weave approaches include Presentation-Abstraction-Control and Frame-Oriented Programming; these all turn a problem domain into a lattice and in doing so approach programming with piss and vinegar. You can try to build systems this way, but my caution is that you will end up with lots of "EDI data formats" and no common media types. Going this route, I feel, is sticking yourself in the 1980s. I could be wrong.

One way to sum this up is to say that "design pattern" is primarily a social and aesthetic phenomenon, and only indirectly a technical one.

No. It is just software is hard. Software is only indirectly technical.

Un chien adalou

Then you have incorrectly agreed on the problem domain vocabulary between you and your problem domain expert.

I find most of your response to my post to be a total non sequitur.

I'm not sure how a focused discussion on the interaction between design patterns and PLs leads to a lecture on software engineering.

(The expression "teaching Granny to suck eggs" comes to mind too... ;-) )

My normal policy is to simply not respond to posts I think are OT, but I've noticed a trend in recent months for frequent postings that depart a little too far from PLs for my LtU tastes, and I have far more interest in SE practices than some here.

Though yours is far from the worst example in recent days ;-), I thought I'd take this opportunity to mention it and register my opinion as a long-time reader and community member.

I agree. Although I have

I agree. Although I have only really noticed this more recently than "recent months." I would rather say recent weeks, I guess.

[edit: I should be more specific. I have noticed a tendency toward much longer posts, long conversations between only a couple of people, about topics in which I am not interested. I haven't said anything because I have hoped that these discussions are really useful to someone, at least to those involved. But some of them have seemed circular and argumentative enough that I've just stopped reading most posts which seem too long. I don't like that solution, however.

I guess my general suggestion to everyone is to write less and read more. I don't know if Confucius said that, but I presume to say that he would have agreed. We don't have to worry so much about OT if posts are shorter and more focused. LtU has not historically been a good place for extended philosophical argument.]

You asked, "What do I do ?"

You asked, "What do I dO?"

I don't see this as a rhetorical question.

The Big Idea I was presenting is that some problems are not worth solving, even with the elegance of PLT. This may be "teaching Granny to suck eggs" for you, but a quick survey of the US's quest for "Information Dominance" in Very Large Scale Software projects shows some people don't. If you're European, much of this American nonsense won't make sense to you. ;-)

You mentioned languages that go beyond aspect-oriented programming as a possible solution. The only project I know of that does this, HyperSpace, never actually described what their data structures would look like and never produced, as far as I'm aware, a working binary, or even broken code, let alone compilable code. I e-mailed about the project and never got a reply.

I'm extremely skeptical about these lattice-throbbing solutions being something to look forward to (although, I'm sure it will eventually generate something of value). They've failed in the past, and even people who are proposing them ultimately conclude that their big goal is simply composition. For example, read Greg Kiczales papers on abstraction in open systems and similar papers by researchers such as Doug Lea and Doug Schmidt. Yet none of these composition concepts are compared to newer composition strategies - such as Mark S. Miller's Robust Composition: Towards a Unified Approach to Access Control
and Concurrency Control
Ph.D. thesis or Alexey Radul's Propagation Networks: A Flexible and Expressive Substrate for Computation thesis.

It says a lot about the quality of the Visitor pattern and the authors who first described it, that if you take it away, you need to propose some fairly big language systems in response! People who propose these alternatives are doing so for good reason: safe re-entrancy, etc.

As for the Visitor pattern, the idea I like the best for making it disappear is the approach Guy Steele and Eric Allen are taking in Fortress, such as trying to give pieces of code explicit properties like associative and commutative. Making Visitor disappear from a programming language is a very hard research problem.

A rhetorical answer

You asked, "What do I dO?"
I don't see this as a rhetorical question.

In my post, it was very much a rhetorical question.

I was discussing the idea of design patterns and how I think they interact with a PL community, using the example you supplied.
Finding all the ways to "solve" a particular pattern is a lot more specific than I was aiming for.

Habitibility & Piecemeal Growth

Allan,

If you haven't yet, check out Gabriel's book Patterns of Software. Especially relevant to LtU is section II of the book, titled simply, "Languages".

Finally,

pattern languages take a long time before tools catch-up to help improve/support them. As an example, open any book on style guidelines for citing others authors' work. Only recently has CiteULike/CiteSeer and Zotero, as well as others, made this more approachable than opening up a book and trying to differentiate what kind of article you want to cite. This is sort of a dramatized example, though, and not really transferable to PLT.

Thanks

Thanks for the reminder. I have a copy of Gabriel's book lying around somewhere, but I haven't looked at it in a while.

No

When design patterns work in concert (as is almost always the case), then no single block of code will tell you enough information to see how it is used. Therefore, the language itself is not enough - the programmer must understand something deep about the problem domain's semantics in addition to the language's semantics.

But not in book form, and certainly not penned by an aesthetic..

...mind.

Types of recursion, i.e., tail recursion, cdr recursion, car-cdr recursion

Procedure types, i.e., selectors, constructors, predicates

I often wondered when first learning functional programming why there wasn't some sort of consolidation in this way. Through such dense summaries, I wanted to know what vocabulary was important when talking about a functional programming technique or problem.

I think a good question would be if a patterns book would be better than, say, reading Bird's Algebra of Programming. For coverage on folds, how could you write something better? (Really, a serious question.)

design strategies

Perhaps more relevant to functional languages would be something like Eno's oblique strategies. Abstract out a function. Reify a function as an ADT. Encode an ADT as a function. Turn an ADT into a typeclass. Reduce your typeclass to dictionary passing. Pack an irregular data type into a regular one. Split a regular data type into irregular ones. Turn a constructor into a function. Replace a function by a constructor. Pack a dictionary into a phantom type. Replace a phantom type by a function. Write a catamorphism. Define the zero element. Turn a catamorphism into an eliminator. Pass in a continuation. Pass in two continuations. Return a function that takes a continuation. Take a list of continuations. Return an infinite list. Turn the list into a function. Turn control flow into a tree.

Soforth.

Useful in a different way

Eno's strategies were developed as an aid to brainstorming -- ostensibly in a artistic endeavor. While it may be a good idea to apply these kind of mental exercises to difficult design problems, I can' help but feel that Eno's oblique strategies are almost the opposite of design patterns.

To me, design patterns help give structure (more specifially, a name) to a popular design approach. Oblique strategies on the other hand, seem to me to remove structure from a popular design approach.

It feels as if they are at different levels of abstraction. Compare for example, Tuma's "Engineering Mathematics Handbook" and Polya's "How To Solve It".

Refactorings?

A lot of your suggestions sound very action-oriented, i.e., "abstract out a function" and describe ways of transforming code without extending its behavior or harming its correctness, possibly refactoring towards a particular pattern. -- Your list describes how to do something, not what something is.

How to do something is very important. I didn't understand ML until reading Larry Paulsen's book.

Joshua Kereivsky suggests patterns are ideals rather than targets that must be reached, and so his Refactoring to Patterns book discusses how to safely transform code in small pieces. It is very deconstructive and uses test-driven development to prove each deconstructive argument on what the best shape of the code should be.

Ironically, most programmers at user groups I go to think design patterns are typified by how -- a major misunderstanding, as many of the 'how' explanations in the GoF book have hardwired subtle assumptions related to Smalltalk and the Everything-is-an-Object code-writing style.

Generation Gap

As an homage to the GoF author not interviewed, the departed John Vlissides, I thought I'd mention a pattern that is unambiguously PLT:

The Generation Gap pattern: Vlissides noted in his last book, Pattern Hatching, that this pattern was left out of the GoF book because the authors were not convinced of its widespread applicability. After the book came out, many mentioned use cases for this pattern to John, so he included it in this addendum book that described how GoF was created.

Examples of The Generation Gap include things like camlp4 and C# #regions and partial classes (I know, other languages had these features in the '80s, but I can't remember their names and never actually used them).

Generation Gap is basically so fundamental you can't eliminate it, only find better ways to engineer it.