This site will look much better in a browser that supports web standards, but is accessible to any browser or Internet device.
Back in September, Mark Dominus wrote a commentary describing his take on Design Patterns. Although I am impressed with a lot of what MJD has written in the past, I wasn't sure what to make of this one. Ralph Johnson of Design Patterns fame responded and MJD wrote again. I encourage you to read all of the above essays before continuing, because I don't want to misrepresent anyone's position.
The biggest surprise to me was MJD's contention that no one had disagreed with him. I don't know if I actually qualify as somebody, but I'm afraid I have to disagree with MJD in a couple of ways. I'm afraid I also need to apologize, because this turned into a longer rant than I had planned.
Let's start with the assertion that the existence of a Design Pattern shows a weakness in the programming language you use. To some extent, this assertion seems to be intended to be inflammatory. In the original talk MJD gave on the subject, he specifically attacks C++ and Java and uses the need for Design Patterns as his argument.
In the most recent article, MJD seems to have softened his approach, suggesting that it's not that object-oriented languages suck, just that they have deficiencies.
One of the things I pointed out was essentially what Norvig does: that many patterns aren't really addressing recurring design problems in object-oriented programs; they are actually addressing deficiencies in object-oriented programming languages, and that in better languages, these problems simply don't come up, or are solved so trivially and so easily that the solution doesn't require a "pattern".
Although the articles seem to be mellowing, it would be easy for someone reading the earlier articles to misinterpret this concern over differences in language features as language or pattern bashing. Even this statement seems to suggest that the languages in question are inferior.
Honestly, I think MJD has a point here that is worth exploring, but the apparent attack tactics makes focusing on this point more difficult.
In the essay about design patterns of the past, MJD conveniently jumps over the most important part of his analogy. For example, let's take his subroutine pattern. Let me summarize my understanding of his argument:
Obviously, we should have skipped the whole pattern part and gone directly to adding subroutines to languages to have avoided all of this mess. There's just one small problem: we didn't know how subroutines should be implemented in the languages. He uses Fortran as an example. The earliest version of Fortran I used had a problem where subroutines were concerned. Each subroutine had an area of memory set aside for parameters and local variables. This meant that recursion was impossible.
Several programming languages added subroutine support directly. Unfortunately, there were several possible ways of handling parameter passing and local variables. A few possibilities for passing parameters include:
Local variables also had many possibilities:
Some real usage was needed to determine which approaches to these two problem yield the best results. Different languages and systems used different tradeoffs to determine which approaches they would use. In hindsight, many of these solutions probably look ridiculous. But, when the whole field of programming was new, obvious was somewhat different.
Even if we accept the idea that the use of design patterns proves that a language is deficient or bad, using them can still give us ways to talk about the issue as we try to discover a good solution to the problem. The other alternative is to refuse to use languages which are deficient in some area. Since there are no languages that are perfect in every area, this would effectively mean we cannot write any code. Although this is an alternative, I don't find it to be very satisfying.
According to Dominus, many people have jumped him about his use of the Iterator example as a reason that Design Patterns are bad. Some have said that the pattern is too simple, so it makes a bad example. Some have suggested that he just doesn't understand the Iterator. I actually disagree. I think it makes a really good example of MJD's argument. I also see where it serves as a counter-argument as well.
At it's most basic, the Iterator pattern allows traversing a container without knowing how it is implemented. MJD points out that this is a deficiency in the C++ and Java languages because they don't have iteration built in to the language. Perl doesn't need iterators because traversing a list is a feature of the language:
foreach $element (@collection) {
...
}
Unfortunately, this argument falls as soon as you have a container that is more complicated than a list. If I have a list of (references to) lists, then I need nested foreach statements to traverse the whole container. Immediately, I (the user of the container) am forced to know about the internals of this container in order to do something with all of the elements in the container. Unless the language handles every conceivable container, there is no way for the language to contain code for every kind of iteration.
Granted, the ability to be able to traverse a list (or anything that acts like a list) is a useful feature, But, it does not replace the general Iterator concept.
Even on the simple list, foreach cannot handle every way I might need to traverse the list. What if my algorithm requires that I traverse the list backwards. Obviously, I could reverse the list when I pass it to foreach, but this may be unreasonable for a list of a million elements. In C++, I can get a reverse iterator that just walks the container in the other direction. By reusing the concept of an iterator as separate from the particular method of walking the container, I can have simpler algorithms to work with my containers.
This can go even further with iterator adapters that modify the behavior of an iterator to allow traversing every other item or every nth item. This kind of behavior is useful in many algorithms. By defining the traversal method in the iterator, we can separate it from the work we are performing on the elements.
This does not mean that the Perl approach is a bad one, direct support for simple iteration is a good idea. In fact, I believe Ruby supports performing this kind of iteration on any container that provides the for ... in construct which provides language support for traversing any container that provides an each method. This simplifies the traversal of a container for the most common case.
There are problems with tying the traversal directly to the container like this. One problem is that there is no easy way to traverse two containers at the same time. Another problem is that it is difficult to traverse one container with two pieces of code at the same time.
When I first discussed Design Patterns with a friend of mine, he discounted them as unimportant. This guy had been programming for decades and had implemented many of the design patterns to solve various problems in multiple languages. I was a younger programmer, and was interested in the different patterns to solve problems I was looking at. One day, Rick said he had had a change of heart about Design Patterns. He said that for him, it was not the solutions that were important, it was the fact that we now had standard names for them.
This explanation really clicked with me. As I developed more experience with Design Patterns and the newness wore off, this part became more important than the implementation of particular patterns. Since then, many languages (or libraries) have implemented these patterns directly. As a user of the language, I can just use these features. I do not have to re-implement them. When a language or library says they implement an iterator, I know the purpose of that feature immediately. I may still have to explore some of the features of this particular implementation, but the general gist of the feature is conveyed by the name.
This ability to talk about code a the level of Design Patterns is a fair portion of what I think MJD was saying that Alexander originally meant by patterns. In fact, I suspect that most of the users of patterns today do not implement those patterns. These patterns are encoded in the standard libraries of the languages they use. As such, they have effectively become part of the language.
While I agree that Design Patterns are not a solution to every problem, I don't see them as proof that some languages are bad. Just because some programmers in one language or another overuse Design Patterns does not mean that the patterns themselves are a sign of bad code.
Granted, there are examples where some people have made a point to try to use every pattern in the GOF book in a single program. There are also examples where people have used patterns inappropriately just to have patterns in their code. If we judge features or languages solely on whether they have been misused, then every feature in every language should probably be declared as bad.
As I've said, I believe that the most important feature of Design Patterns is the standard naming provided. This allows us to talk about similar solutions to similar problems. Design Patterns also allow us to provide standardized solutions to similar problems when they arise. For example, the C++ standard library provides several useful containers and iterators to traverse them. If I develop a new container, I can provide iterator support for my container allowing stand algorithms to work with my container. This also simplifies other people learning how to use my container.
Posted by GWade at November 4, 2006 09:54 PM. Email commentsAfter reading this article and the cited references, I am left with the feeling that MJD seeks the holy grail of a “perfect language”. I am a bit a cynical when it comes to the subject of perfection in a given computer language. I am reminded of that somewhat irritating video of the Ruby on rails video where the speaker demonstrates how “easy” it is to put together a blog with this platform in just 15 minutes.
( http://www.rubyonrails.org/screencasts )
As you watch him put this together, anyone with a smidgeon of experience knows how much detailed knowledge of rails the speaker has to “make it look so easy”. To get to that point, requires experience and quite a few “woops!’s” along the way. IF MJD wants to summon a midget with a kneecap fetish, there is quite a bit of work to be done.
This would best be suited with domain specific language rather than a general language that has built in support for the “stuff of your dreams”.
Don’t get me wrong, I would love to have a 3d graphics platform that I could just “talk to” and say, “Create Sphere…3 units. Squash x axis 3 pixels.” It should be apparent that the verbs in this platform could grow and grow and just to learn the vocabulary would defeat the purpose of simplicity. Of course, an experienced developer in such a platform would eventually be quite productive. The problem/trend with languages is that by the time you master it, find it’s quirks and workaround, someone comes along and fixes those quirks so workarounds are no longer required and the “master of the language” becomes the victim of what I call “knowledge erosion”.
Unfortunately, I have strayed from the design pattern subject at hand. The only thing I have to add is that design patterns are intentionally language independent. There is a special genius to this concept. I think making them intrinsic to the language can only go so far. Your example of the perl iterator is a good one and I particularly liked the insight for the case of the “simple” iterator. I also think it stands as a strong argument that trying to be all things to all programmers is counter productive and violates the concept of factoring. There is a awkward balance between power and simplicity.
I think of a cable show I like called “the most extreme machines”. Even the most powerful machines today have a lot of small moving parts that are wired together so that a TRAINED operator simply pushes button x or pulls lever y. The operator of such a machine has to be licensed, trained etc, but probably doesn’t know much about the hydraulics or electrical wiring that lay beneath the machine he commands. The mechanical “wrapper makers” have seen to it that he does not have to. .. But I digress.
One last point, I swear . In the few years I have been a programmer, I see that constant effort to dumb down the programmer so that “all ya gotta do is..”. In many cases, the prefab combobulation is always missing some feature or worse, has an egregious bug that I have had to either live with, fix, or roll my own.
Great post GWJ.