Grokking software patterns
Programming, Software Patterns May 15th, 2007 - 7,999 viewsIf you’ve been programming for any period of time then you’ve probably heard about these things called patterns. But unless you’re a professional you’re probably not quite sure what they are, or what they’re good for. I’ve decided to start cataloging some of the software patterns I use most frequently in my projects. But before I do that, I figured I should try explaining what a software pattern is.
A Quick History
It all started when, in 1987, two fellows named Kent Beck and Ward Cunningham had some trouble finishing a design for a consulting gig. The pair had been reading a book called A Pattern Language that was written by a relatively obscure architect named Christopher Alexander.
Alexander had gone through a bunch of traditional architecture and found common elements (e.g. Roof Garden, Main Entrance, Bathing Room, Open Stairs, etc). The idea was that by cataloging these “patterns” he could make it easier for architects to communicate. It’s no surprise then that programmers, who generally feel that every problem can be solved by simply adding another layer of abstraction, jumped at the chance to use abstractions to communicate. Ward and Kent came up with a small set of common UI patterns, then let the users design the interface themselves using their new pattern language - and apparently it worked.
Skipping ahead a few years to 1995, a Gang of Four (GoF) fellows named Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides who were inspired by Kent and Ward’s success published a book called Design Patterns. This is probably the most influential object-oriented software development book ever published (which puts it at about #57,283,287 overall). And the rest, as they say, is history.
So, wtf is a pattern then?
It’s simple really… so simple that it’s hard to explain. A pattern is an idea that has been useful in at least one practical context, and will likely be useful in others. A pattern won’t solve a problem, but simple describes how to approach the problem in a way that makes solving it easy. Patterns are rooted in practice, not theory. They’re not produced by academics in ivory towers, they’re recognized in existing solutions and cataloged. Martin Fowler, a famous “pattern hatcher,” explains that he discovers patterns, he does not invent them.
Patterns come in many forms, reflecting varying degrees of abstraction and specialization. Design patterns are probably the most commonly discussed software patterns. This type of pattern solves a recurring problem in software engineering, and typically shows relationships or interactions between classes or objects in a system. There are also analysis patterns, which reflect conceptual structures of business processes (like how to represent a quantity or range) rather than actual software implementations. Model patterns describe different approaches to data modeling, and architectural patterns express a fundamental structural organization schema for a software system (like the Model View Controller [MVC] pattern).
If you’re still having some trouble understanding what a pattern is, this example from Design Patterns might clear things up:
Novelists and playwrights rarely design their plots from scratch. Instead, they follow patterns like “Tragically Flawed Hero” (Macbeth, Hamlet, etc.) or “The Romantic Novel” (countless romance novels). In the same way, object-oriented designers follow patterns like “represent state with objects” and “decorate objects so you can easily add/remove features.” Once you know the pattern, a lot of design decisions follow automatically.
What makes a pattern
There are four essential elements of a pattern:
- The pattern name is a brief phrase that represents a complex problem, a solution, and any relevant consequences. This is the abstraction part I talked about earlier. Once you’ve given a pattern a name, you don’t need to describe it any more. A paragraph of text is potentially replaced with a single phrase (e.g. let’s make the database class a singleton).
- The problem is the grounds or motivation for the pattern. It describes when a particular pattern should be applied. Oftentimes the hardest part of using patterns is recognizing a problem and realizing that a pattern exists to solve it.
- The solution is an abstract description of how the problem is solved. It’s not a concrete implementation. The solution is not code - it’s not even pseudocode. Rather, it explains how to approach the problem in a way that makes solving it obvious.
- The consequences are the trade-offs that you make by selecting one solution over another. For design patterns the consequences are the typical space and time trade-offs seen in computer science. Consequences also include the impact on a system or component’s flexibility, ease of reuse, extensibility, and portability. Many patterns have alternatives, so consequences can also help you identify which pattern to use.
Why use software patterns
So why should you care about patterns? Well, let me explain…
First, patterns document best practices. Instead of discovering the proper architecture for a piece of software the hard way, patterns tell you up front what the best solution is. Like I said, patterns come from practice. So by definition a pattern has been used to successfully solve a problem at least once.
Patterns are also typically composable, which means that they’re easy to integrate with other patterns. In fact, most pattern descriptions will list other patterns that are particularly suited for interaction. Patterns not suited for interaction (if any exist) are typically documented as a consequence of using a particular pattern. So by using patterns you’re improving your architecture, and applying best practice solutions in a manner that feeds on itself. And if a pattern reduces flexibility in some way, at least it’s documented and well understood.
Finally, patterns are language independent solutions that will transfer, with minor modification, to a variety of situations. In five years, when Ruby on Rails has fallen out of favor and Java is just a distant memory you’ll still be able to apply patterns. And next time you’re trying to explain the architecture of your new web application to a colleague you can skip the lecture and simply tell them that “it’s built around a Front Controller and uses a Template View.”
If you’ve ever wondered what separates a good programmer from a great one, patterns are it. Good programmers can write pretty much anything once you’ve told them what you want and given them an example. Great programmers have learned through experience how to implement functionality without looking at someone else’s code for inspiration, or using a recipe. They have a deep understanding of how various types of components interact, and what combination of relationships and behavior produce high quality systems. Patterns encapsulate this knowledge so that the rest of us can be great too.
If you’re interested in learning more about patterns, make sure you subscribe to my feed. I’ll be posting descriptions and example implementations for patterns that I’ve found particularly useful in projects that I’ve worked on.
May 15th, 2007 at 10:03 pm
“If you’ve ever wondered what separates a good programmer from a great one, patterns are it.”
Possibly, but in the opposite way than you mean:
Patterns are a step in thought, not an ideal solution. If they end up being present in code, it’s because the language or the developer are not sufficiently advanced. For example, the visitor pattern is completely unnecessary in Lisp or Smalltalk, and probably Ruby and Python, because the language natively supports multiple dynamic dispatch, which the visitor pattern is a poor man’s emulation of.
The singleton pattern - instantiate a class exactly once and you have a singleton. There’s no need, around competent programmers, to set up barriers to prevent them from instantiating the class more than once, as people often do.
If you code in patterns, then you are coding repetitively. Try not to do that.
May 15th, 2007 at 10:37 pm
I agree with you that patterns are “a step in thought.” A pattern is a useful starting point, not a destination. As Martin Fowler often says, patterns are half-baked - you have to finish them yourself.
The rest of your comment, though, I don’t really understand. Sure there are certain patterns that are unnecessary in certain languages. The GoF said in their intro that if they had used a procedural programming language, “they might have included deisgn patterns called ‘Inheritance,’ ‘Encapsulation,’ and ‘Polymorphism’.” Calling polymorphism a “pattern” in Java doesn’t really make sense though: it’s a feature of the language.
That doesn’t mean, however, that a pattern will not or should not be present in code. If a pattern was never present in code then it wouldn’t be a pattern - an existing implementation is a requisite condition in every definition of a “pattern” I’ve ever seen. Working with software tools (i.e. programming languages) that are not sufficiently advanced is a fact of life. There are always trade-offs (whether time/performance, maintainability/scalability, or otherwise), and there is never a perfect solution - just better ones and worse ones.
Re: your comment on the singleton pattern, again I’d have to disagree. Either way you need some mechanism to ensure the class is not instantiated more than once. Your way relies on a human who, competent or not, is fallible, especially when working with massively complex and multifaceted problems. Using a pattern relies on the computer to enforce this rule which, I hope you’ll agree, is much more reliable.
Perhaps more importantly, using a singleton is self-documenting, and improves the maintainability of an application. Programmers who are familiar with patterns will recognize the class as a singleton, and know what that means. Programmers who aren’t familiar still can’t break the application, because it’s impossible to instantiate the class more than once.
Finally, higher level architectural and analysis patterns must be present in code. They cannot be features of a programming language, they are essential components of a program, or conceptual models that describe an application’s organization.
Don’t mean to sound rude or anything… definitely tell me why I’m wrong!
May 15th, 2007 at 11:16 pm
That certain patterns are unnecessary in certain languages shows that patterns are for those areas where your language forces you to repeat code.
The literature around patterns makes them sound like a goal; I’d rather treat a pattern as an emulation of a language feature.
The problem with a singleton is intent. You intend your class to be a singleton for your project’s overall scope, but it makes sense for each automated test to start with a fresh instance of that class. The intent may vary depending on how your code is used. That’s one reason why actively preventing instantiation is not a good approach - it limits reuse.
I’d rather trust programmers enough - provide them with the instance via an IoC framework (even a hand-rolled one - that’s really quite easy to do [1]), but don’t actively prevent them from making their own instance.
You’ll find that the more you treat programmers as intelligent lifeforms, the more like intelligent lifeforms they’ll behave.
Another way of saying this is that by allowing programmers to make mistakes, you will encourage them to learn. That’s not the same as tricking them into making mistakes, of course. Try to make the most natural path the correct one.
What higher level architectural or analysis patterns must be present in code, but also must be patterns (and therefore repeated in code, rather than abstracted away)?
This comment’s getting a bit long, I can feel a blog post coming on.. , but one more thing - it makes sense to apply testing and static analysis to the changes that people make, but if you ultimately feel that you can’t trust them to do the right thing eventually, keep them off the codebase. You can’t protect the codebase while allowing write access; you’ll just end up shrouding everything in beurocracy.
[1] IoC itself can be abstracted away - dynamic variables as Common Lisp supports get rid of the need for IoC frameworks.
May 15th, 2007 at 11:52 pm
Ok, I have a better idea of what you’re saying now. And I think you’re right, to a certian extent, and with respect to certain types of patterns.
Many design patterns can be incorporated into languages in such a way to make them no longer interesting. Iterators in any modern language (Java, C++, even PHP) are a good example. Alternatively, class libraries can make implementing a pattern trivially easy (e.g. Java’s Observable). My only counter here is that if you make everything a language feature you’ll end up with one hell of a bloated language. At a certain point the language designers are going to have to stop and the application developers are going to have to pick up, and no matter the level of abstraction, patterns will still exist. Plus, I still don’t think that it’s bad practice to use a pattern just because your language doesn’t directly support it.
I still think that the singleton pattern is useful - I use it all the time. It’s not a matter of trust, but maintainability and ease of use. There’s nothing wrong with making things easier on yourself. I’ll leave it at that, since the singleton pattern is one of the first I wanted to cover - we can pick things up from there :).
There are many high level patterns that I don’t think belong as part of a programming language. Architectural patterns like Front Controller, Plugin, and Query Object might make sense as part of a framework, but not as part of a language. As for analysis patterns, I can’t think of any that could become language features. It wouldn’t make sense, they are far too domain specific.
I think we’ve both already written a blog post here - maybe we should do a debate piece :).
May 16th, 2007 at 12:41 am
You only end up with a bloated language if you design the language badly. Common Lisp is somewhat bloated, but most of that is due to the way the standard was formed. Lisp as a concept (and probably Scheme) can support all these things without being bloated. Smalltalk is probably the same, and maybe Python, Ruby and Haskell too.
Advanced languages allow you to extend the language by writing your own constructs. E.g., in Practical Common Lisp you get to write your own SQL-like select..where-like construct, in the third chapter. So the innovation doesn’t have to stop where the language designer left off.
“Plus, I still don’t think that it’s bad practice to use a pattern just because your language doesn’t directly support it.”
I agree with that, but at the same time the bad feeling when you repeat code, or similar code, should stay. Feeling good about using a pattern might get rid of that bad feeling. I’m sure Hani would call it navel-gazing. Looking at Java, some developers would actually rather repeat the resource acquisition and release pattern many times than add support for encapsulating it to the language.
(You can encapsulate it currently, but it’s ugly to do so, so not very many do).
I don’t understand the front controller pattern enough to see why it would result in duplicated code.
The plugin one is interesting - if you allow third-party code to be treated by your runtime as if it were just your own code, then you don’t have to do anything special to achieve this. In Emacs, you can redefine the internals - you don’t need to specifically interact with a plugin API, just with the system itself.
Query Object - that will be first against the wall when the revolution comes - the specific revolution being the abolition of SQL. I think as more and more databases are written in the languages that their clients are using (or interoperable languages) then more direct access will be demanded.
May 16th, 2007 at 12:43 am
I should correct myself a little - You only end up with a bloated language if you design the language for the short term, but use it for the long term.
Java is a nice example of this - lots of features like synchronized that look short-sighted now (and probably did to domain experts at the time).
May 16th, 2007 at 8:43 am
Ricky, the intent of the singleton pattern is not to prevent bad programmers from making multiple instances of a class. There are many uses. For example, have you ever had a large collection that needed to be updated from multiple threads (for example — a web application where many users are updating a large table, simulataneously)? A database may be inappropriate for performance reasons, and you may be forced to use a collection to store your data set in resident memory. The singleton pattern is ideal for this situation — a single instance of the object (a collection, or a DOM) which can be accessed from multiple clients. Do you have a better solution than a singleton?
May 16th, 2007 at 9:09 am
How is extending a language different from programming? I can extend C by writing a function. If you can easily implement a pattern by extending the language you’re working with then that’s great, but you’re still implementing the pattern.
I’m not so sure about the fall of SQL. There are certainly a lot of problems with the language, but it’s a standard that is heavily used by probably every major corporation and government institution in the world. These guys don’t exactly move fast.
Even if SQL did go away, however, that doesn’t make the Query Object obsolete. The Query Object is an implementation of the interpreter pattern that is typically used to manipulate data using SQL. But it doesn’t have to use SQL. The fact that it typically does is simply a result of SQL being the common paradigm these days.
May 16th, 2007 at 10:29 am
Prichard, I think you’re confusing singletons with global variables. If not, then I propose the global variable as being a more direct solution than the singleton for the case that you present.
Mike, extending a language is not different to programming. However, some languages are deliberately built to be extended. You can write a function in C, but you can’t make switch statements work on non-integral types, you can’t use operators as the names for functions, etc.
The important difference is how often you have to implement the pattern. Can you take your Java code for a singleton and generalise it so that instead of ‘class Blah’ you write ’singleton Blah’ and all the repeated code isn’t necessary? No, because Java is not that extensible a language. (Scala supports singletons directly in code).
With SQL, you’re correct, it’s a standard, but huge corporations and governments don’t tend to drive innovation (perhaps that’s untrue when the military is involved). Look at Java - the JDK includes Derby - I expect there are some performance gains to be made from accessing Derby’s data structures directly from Java. Not only that, but some expressiveness gains. The need for an interpreter between the application and the database may disappear.
This is only based on the notion that everything mundane in technology can be abstracted away and often even completely removed.
May 17th, 2007 at 1:43 am
[...] just came across a blog entry by Mike (who I don’t know) about design patterns, which I found a very interesting read. The ensuing [...]