The Problem with Patterns

gsmith | programming | Tuesday, July 1st, 2008

There was also a problem with popplers, but it is not related.

(Note: I later wrote this post, which comes to a better conclusion than this article did -Greg)

I’m not a big fan of design patterns.  I know, I know, what could be bad about established processes for solving equally established problems?

I’m not going to complain about misuse of design patterns.  I can roll my eyes at overuse of Singletons, but can’t say overuse is the pattern’s fault.  A lot of people misuse the Factory Method Pattern.  Sometimes people seem to just use this pattern for the hell of it, when a normal constructor would suffice.  Still, these people are Doing It Wrong, so it is their own fault.  I’m not even going to complain that people use patterns as an excuse to not really think through their problems and figure out the best thing to do.  Like I said: This is not what is wrong with design patterns.  This is what is wrong with bad programmers.

So what is wrong with design patterns?  Let’s start by analyzing a few examples.  I’m going to draw my examples from the ubiquitous Design Patterns: Elements of Reusable Object-Oriented Software.

Lets look at the Command Pattern.  In it “A command object encapsulates an action and its parameters.”  I always call this the Functor Pattern, because I call these things Functors.

In a lot of languages, this doesn’t even constitute a design pattern.  The idea of functions as first-class objects, specifically functions that can be assigned to values, makes doing this a fundamental part of programming in that language.  For that reason, we can adjust the definition of the pattern:

“A command object encapsulates an action and its parameters.  This is useful in languages that don’t allow function values.”

Here is what is interesting: The pattern is not about programming.  It is about programming in this particular language.  In some other languages, the pattern is meaningless.

Let’s look at another example.   The Factory Method Pattern is about hiding the concrete class of an object.  Look at this constructor (in Java):

class DatabaseConnection {
    public DatabaseConnection() {
        ... do some set up work ...
    }
}

Lets say we want to implement both MySqlDatabaseConnection and SqlServerDatabaseConnection.  But we want which one we use to be defined in a configuration file rather than in the code.  Normally you would create a constructor for each of these classes, but then, when you want to instantiate the database connection, you have to pick one.  You can’t do this:

class DatabaseConnection {
    public DatabaseConnection() {
        if (Config.getConfig().getDatabaseType().equals(DatabaseType.MYSQL)) {
            return new MySqlDatabaseConnection();
        }
        ... else if etc ...
    }
}

Doh!  Constructors have no return values, so this is just bogus, though it might seem like what you want to do.  The Factory pattern is the way to do this that actually works.

But stop.  See what happened there?  We are using a design pattern because we can’t do what we want to do.  If Java constructors worked a little differently, and let us construct an object of an arbitrary subtype of the type the constructor lives in, there would be no Factory pattern.

As you begin to analyze the various established Design Patterns, you start to notice this type of thing more and more.  A lot of them solve problems that are imposed by the programming language you are using.  Some ways to recognize such patterns are:

  • The pattern is too trivial to mention in a different programming language
  • The pattern results from being unable to do what seems naturally correct

Not all design patterns are like this.  The Flyweight Pattern, for example, seems to basically reduce to optimizing the time-space trade-off of a group of many similar hashes.  This is more of a data structures thing than most Design Patterns seem to be.  It is also particularly interesting and useful, in my opinion.

So, going to sum up and draw some conclusions.  It is no accident that Java is the language that brought Design Patterns to the forefront of our minds: It also is a language that loves to force you to do things in very particular ways, especially as opposed to simpler and more elegant languages.  Many design patterns emerged as ways to do things that aren’t specifically considered in Java’s Way Of Doing Things.  And that is what frustrates me about them: I’d really rather just use a language where it is easy to do what I want to get done.

Of course, I’m not going to get into a discussion of the advantages of strictly constrained ways of doing things, or dynamic versus static typing, or anything like that.  Suffice it to say: I understand there are advantages.  However, these advantages don’t do much to reconsile my enjoyment as a programmer.  And I admit: I’m a little selfish that way.

By the way, the picture for this article is a Poppler.  There was a problem with them too, but it is not related to the problem discussed in this article.

8 Comments »

  1. You misunderstand why the factory pattern is useful and what it’s for. It’s not fixing broken constructors in Java, Smalltalk constructors work just like you envision, you can return any type from them, and yet we still need the factory pattern.

    The point of the factory pattern is to provide overridable hooks so subclasses can inject different concrete classes into the superclass’s algorithms. I might subclass UIComponent and override the renderingCanvas method to return my own custom canvas that draws things how I like. The point really is to not have naked class references in the middle of methods anywhere because that’s not very flexible; extracting naked class references into a separate method gives the *option* of overriding it to future subclassers who may want to do things differently.

    The command pattern is also not just trying to emulate functions in languages that lack them. It’s trying to emulate a closure, a function with state. A closure is an object with one function and an object with one function is closure. This is important because you want to be able to serialize commands. A database transaction log is a good example, a sequence of commands serialized to disk that when replayed in that same order later will rebuild the state back to the point the crash occurred. You can’t do that with just functions, you need closures/objects.

    Half the examples in the design patterns book were written in Smalltalk, a language which suffers none of Java’s silly restrictions. What they are however, is patterns for object oriented languages, so they may not apply if functional/procedural programming is your thing.

    Comment by Ramon Leon — July 19, 2008 @ 1:17 am

  2. What you describe is a valid use of a Factory method. But still, what you say is not really any more interesting as a pattern to me. It is just inheritence.

    It is certainly confusing that there is supposedly a “Factory Method” pattern and a “Factory Pattern” and all these different takes on it. I guess if you take it just to mean “a method intended to create and return a new object” you’re not limiting yourself to any particular purpose. The definition I was addressing is “Using methods to abstract away the concrete class of the constructed object.” The pattern is the goal and the technique.

    As for the command pattern: You say it right when you say it is “trying to emulate a closure.” You don’t try to emulate these in languages that support them natively. That is what I’m trying to get at. You’re working around something your language doesn’t do.

    As for design patterns for Smalltalk: I think if the pattern makes sense for Smalltalk it probably does stand a better chance of being truly interesting. Smalltalk is a fine language that doesn’t have a lot of Java’s problems. Some design patterns applicable in Java are not applicable in Smalltalk for that very reason.

    Comment by gsmith — July 19, 2008 @ 1:41 am

  3. The command pattern is not necessarily working around lack of closures. Smalltalk has closures, but closures alone don’t make a command pattern and closures in most languages aren’t generally easily serializable because of how they capture the local environment. Consider an undo redo stack, a command would encapsulate both the action required to execute it, and to undo it. Commands get much fancier than just execute them. It’s easy to think they’re just making up for something missing in the language, but they aren’t, they’re a description of an object model that is hany, in *any* OO language, even those with closures, but that isn’t the case, it’s more complicated than that.

    ‘I guess if you take it just to mean “a method intended to create and return a new object” you’re not limiting yourself to any particular purpose. The definition I was addressing is “Using methods to abstract away the concrete class of the constructed object.”’

    Those two sentences mean virtually the same thing. The sole purpose of extracting new object creation into another method is to hide the concrete class, you wouldn’t do it otherwise. Of course it’s just taking advantage of inheritance, that’s a huge part of what OO is, and that knowledge wasn’t generally accessible to most people prior to the design patterns book, it popularized a common vocabulary for object models that helped make OO widely known.

    You are right about some of the patterns being unnecessary in other languages, but you chose bad examples. Both factory method and command patterns are widely used in languages that you can’t say lack the right features.

    A better example would have been the factory pattern itself, it’s generally only useful in languages lacking *real* meta-classes. In Ruby or Smalltalk, you’d use the class itself as the factory for its instances and you can add methods to it, and pass it around, classes are real objects, making factories unnecessary. Or the strategy pattern, which is really just a way to fake anonymous methods for specializing higher order functions.

    Comment by Ramon Leon — July 19, 2008 @ 5:57 am

  4. So, Java in fact is not the language that brought patterns to my mind, C++ was and the folks from whom I learned C++ patterns were explicitly importing into that language culture a style of knowledge management that they had learned in the Smalltalk world.

    Note that “Design Patterns: A Smalltalk Companion” exists and that it devotes 14 pages to Factory Method. I have a subtle disagreement with Ramon: in Smalltalk programming it’s not that Factory is unnecessary, it’s that it’s ubiquitous. Not that Smalltalk doesn’t require you to add utility creation methods to a class class, but almost all programmers do almost all the time.

    It think that what you are trying to do, identify patterns that are more interesting because they are not so much a work-around for missing language features is an interesting approach—and greatly more sophisticated that the average “critique” of patterns. And yet, would Flyweight seem so interesting in a language with symbols and automatic memoisation?

    Or, consider that one collection of Smalltalk patterns contains a write–up of “Abstract Class”, which would be irrelevant, or be invisible or whatever dismissive adjective you like if only Smalltalk had…abstract classes! As it is invisible in Java.

    I submit that there are patterns of use in every language, because no language has every kind of way of organising code built in. Even in the languages with a culture of programming extending the language we still find repeating idioms that by convention are not (for example) captured as a macro.

    Comment by keithb — July 19, 2008 @ 11:17 am

  5. Ramon Leon:

    Yeah, I can see where my examples could have been better. This is a useful critique to improve my argument. Thanks for taking the time to explain where you’re coming from.

    keithb:

    Good points here. Especially about Flyweight: never thought of it that way.

    I definitely agree that there are patterns in every language. I’m being careful not to suggest that there are no “real” patterns, or that all patterns are in some way “bad.” My point is a weaker statement: That some languages have more of them because of how inflexible the language is. In that case, you get many, many patterns, some of them solving fairly simple problems.

    It is also worth considering the difference between patterns that exist because of a lack of a feature and patterns that exist because of a constraint. I personally believe that languages should be simple, elegent, and flexible. I’m not much for “features”, at least not the type that can’t be expressed as macros or libraries. I suppose any pattern can be obsoleted by adding features, but more interesting are the ones that can be obsoleted by removing constraints.

    Comment by gsmith — July 19, 2008 @ 3:12 pm

  6. Programming is a form of expression — you are expressing what you want the computer to do. The programming language is the way you express what you want. If we had a perfect form of expression, the computer would always be able to tell exactly what we wanted and we would have *no* problems with programming.

    Therefore, *all* programming problems can be seen as problems with a programming language, since all problems *could* be solved at the language level. In the real world, we have to be pragmatic and use the flawed languages that we have available. These *all* have recurring problems, which result in recurring solutions. These solutions are then generalised into design patterns, which are applicable across various languages that share similar features, but certainly do *not* apply universally.

    I’m pretty sure this is explained in the first few chapters of the GOF design patterns book, but since you seem to think it was based around the problems with Java (when, IIRC, it used C++ and Smalltalk), I’m guessing that you never actually read it?

    Comment by Pete — July 19, 2008 @ 4:15 pm

  7. Pete:

    This could have been an interesting contribution, which is why it is too bad you chose to end it with an inaccurate and rude insinuation.

    I maintain that there are good design patterns in any language. My point us that some are more universally helpful and that some languages are prone to needing complex solutions to simple problems. This doesn’t seem to contradict what you point out.

    It seems it would have been better to title this article “The problem with pattern-prone languages.”

    Comment by gsmith — July 19, 2008 @ 4:54 pm

  8. [...] The Problem with Patterns [...]

    Pingback by incompl.com » Pattern Prone — July 23, 2008 @ 2:44 am

RSS feed for comments on this post. TrackBack URI

Leave a comment