Design Patterns Suck
23 points by sspaeti
23 points by sspaeti
Design patterns are a bestiary. You encounter them in the wild, learn to recognize them, describe them, and name them, so that you can benefit from common language.
Thinking "what design pattern should I use" is absolutely the wrong way to solve a problem you encounter.
I agree, they should be. But I also noticed that they are often used as templates instead.
used as pokemon - gotta collect them all
it can't possibly be good software if you haven't implemented all of them in your 200+ microservices architecture. how else will you get your sr. principle architect promotion?
This whole post is just uninformed nonsense.
It's the language that practically forced design patterns into mainstream use. Why? Because Java is verbose, stiff, and lacks expressiveness. It's like writing a novel where you're only allowed to use three-syllable words — every problem feels ten times harder than it has to be.
Why then, are they so used in ruby, arguably one of the most expressive languages around?
Design patterns understood as copy-pasteable solutions of code are indeed stupid, but that was always a misunderstanding of what they were about. A singleton pattern is a singleton whether you use a language keyword that ensures a single instance or you create it yourself with whatever affordances your language gives you. The same is true of Factories, Builders and whatever else. The reason for that is that a pattern is basically a description of a problem and the forces that are in play. In language-specific pattern books you will also see some code. The code is not the pattern. The code is one solution to the described problem within the space described in the pattern.
The claim that "the language that practically forced design patterns into mainstream use" is more or less the same that Ward Cunningham, the creator of design patterns, has:
The job was really to take C++, which was a fairly static language, and show people how to write dynamic programs in a static language. That's what most of the patterns in that book were about. And in the process, patterns extended the life of C++ by a decade, which is not what I thought would happen. What I thought would happen is people, when they learned these patterns, would look at them and say, "Wow, these patterns are hard in C++ and they're easy in Smalltalk. So if I want to think in terms of these patterns, I might as well use a language where they're easily expressed." And extend the life of Smalltalk by a decade. But the opposite happened.
The code is not the pattern. The code is one solution to the described problem within the space described in the pattern.
I agree with you that the code is not the pattern. But not sure I would call them descriptions to a problem. In some languages what they 'solve' are not even problems! The value of patterns give us vocabulary to talk about concepts. But the important thing is the concept itself. e.j. Singleton => global state
Would you call mapcar/fold a design pattern (Iterator)? I wouldn't. The iterator pattern is something that I need to implement to work around the weakness of the language I'm using. The important thing are the concepts behind them. But during the 90s there was a fetishization of design patterns, to the point that every little variation was given a name. They were like pokemons!
Design patterns understood as copy-pasteable solutions of code are indeed stupid
The problem is that this is the common understanding. You get funny looks when you tell someone that a function can be a factory.
I don’t think I agree. If I had a keyword to express a singleton pattern it would not be a “pattern”, no more than how much a while loop is a pattern in a high level language. If I’m using C, while is a core concept. If I’m writing assembly I have to express the same concept with a common pattern. In functional languages where you have pattern matching and map/fold etc you don’t talk about the Iterator or Visitor patterns because that’s not needed. Using monads to express effects on the other hand is a functional design pattern needed in those languages because those languages cannot natively express those effects, while for sure you don’t need any pattern to express “mutation” or “I/O” in C.
Sort of. It’s still the same concept though and there’s still the question of when to use it. Nb scala has the object keyword which creates a singleton.
Why then, are they so used in ruby, arguably one of the most expressive languages around?
That's not evidence of anything. The Ruby object model is an "advanced" topic, it isn't universally understood. There are people who write Java style code in Ruby, even when it's redundant. The recognizability of the pattern itself might be a feature, since it's what people are familiar with.
A singleton pattern is a singleton whether you use a language keyword that ensures a single instance or you create it yourself with whatever affordances your language gives you.
They're always working around design limitations of the language.
Classes are natural singletons in a lot of languages. Guaranteed by the compiler. Why not use them? Because they're second class citizens. Classes can't do things like implement interfaces and be passed around as parameters, only objects can. So you have to retrofit the compiler guarantees into ways that play nice with the semantics of the language by defensively conjuring up an instance that ensures its own uniqueness against all possible users.
Why use factories? Because new only ever returns concrete types. Writing new X only ever returns instances of X, it can't ever return a subtype, it writes X into the bytecode and therefore hardcodes it into the ABI. You write methods to hide new inside a function.
Ruby has none of these problems. Ruby classes are objects that can respond to arbitrary methods and can be passed around just fine. Ruby doesn't even have a new, it always uses factory methods, since new is just a method of Class, which one can just redefine and return whatever. Everybody uses factories without even realizing it.
Bob Nystrom illustrates factories in language design beautifully in his blog:
This whole post is just uninformed nonsense.
Indeed. First, he says "Take the Singleton pattern as an example." And then he butchers and over-complicates an implementation of the same, in an obviously thread-unsafe manner. Over half of the example is garbage. Here is the corrected version:
public class Logger {
private Logger() {}
public static Logger getInstance() {
return Singleton.INSTANCE;
}
private static class Singleton {
static final Logger INSTANCE = new Logger();
}
}
(This approach was developed by a friend, CrazyBob, a few decades ago.)
The real problem is that the author of this blog is complaining about various aspects of the design of the Java language, while he is saying that he is complaining about "design patterns" -- from a book that ironically predates Java, and reflects more on Smalltalk.
Want to see how Scala handles it?
Not really. But again, this blog isn't about patterns; it's about being a language fanboi.
I stopped reading at the factory pattern where he claims factories are unnecessary if you have good constructors.
The main thing that makes factories unnecessary in Smalltalk and Objective-C, but necessary in C++ and Java is that the former do not have a language-level notion of a constructor. A constructor in Objective-C or Smalltalk is simply a method that returns an instance of the class or something compatible. The latter part means that it can return a different concrete subclass depending on the values passed during construction. This, in turn, is used to implement the class cluster pattern (oh, look! A design pattern!) where a single class defines an interface but splits its implementation into different concrete subclasses that are optimised for different uses (Smalltalk’s #become: makes this even more flexible). And that’s a design pattern because the implementations of class clusters are all different but if someone looks at the code and is told that it’s a class cluster then they understand why all of the concrete subclasses are there.
(This approach was developed by a friend, CrazyBob, a few decades ago.)
I don't think the inner class is even necessary: per JLS 12.4.1
A class [...]
Twill be initialized immediately before [...] astaticmethod declared byTis invoked.
So Logger is initialised on the first getInstance call, and that's the point at which static fields of that class would be initialised. Hell you could probably drop the method.
The only situation where I could see the inner class being necessary is if you have other static fields, the access of which you don't want to trigger singleton initialisation, but even then they'd have to be other than constant variables (final fields with a primitive or String type), as those do not trigger class initialisation.
I always figured the most useful part of design patterns was being able to describe an architecture in words and have other people know what you mean quickly due to a common reference point. Especially for patterns that are more than just a language feature (e.g. while language features could make a visitor pattern nicer, I'm not sure a visitor is itself something that would be a language feature.)
Exactly!
I've never understood the "design pattern => solution" approach - I certainly saw it at uni in the software engineering course where you'd swear some people thought the grade was proportional to the number of design patterns they were using.
That is exactly where the original (Alexander’s “A pattern langage”) goes. The GOF may have been poor, and many of its patterns have had limited applicability, but being shared vocabulary as part or parcel of solving problems is the point.
I agree in principle that the whole elevation of "Design Patterns" to the pedestal it stands today is bullshit, but using Python in some of your examples is just shooting yourself on the foot (the language is it's own hodgepodge of bad ideas).
💡 In Lisp, you can override the behavior of any construct. Patterns didn't take root there at all — you just write a macro, and the language bends to you.
A lot of criticism on the whole "Design Patterns" movement in the 90's came exactly from the Common Lisp community and, as it is often the case in this cursed industry, it fell on deaf ears.
Richard Gabriel wrote some good posts when this came up in comp.lang.lisp:
Here's another true statement about patterns: If there are no such things as patterns in the Lisp world, then there are no statements a master programmer can give as advice for junior programmers, and hence there is no difference at all between an beginning programmer and a master programmer in the Lisp world.
https://groups.google.com/g/comp.lang.lisp/c/3GoG3oR9qYA/m/iCHpIBoFP3oJ
But don't dismiss patterns because of the GoF - much of the patterns community derides that work as well. Think of the GoF as helping losers lose less.
https://groups.google.com/g/comp.lang.lisp/c/3GoG3oR9qYA/m/2mXvq-1sDYoJ
Toward the end of this thread were some good and accurate remarks about patterns and pattern languages - especially how you cannot have a pattern really without a pattern language. This is the fundamental error that the GoF - not the first by any means in CS to discover Alexander - started with that led them to the rather damaging book they wrote. Damaging because it made it easy for people to veer away from patterns - such as the Lisp and Scheme communities who would have had a lot to contribute had they not believed they were just a way to get around the abstraction poverty of C++.
https://groups.google.com/g/comp.lang.lisp/c/xSvcQqVKSIo/m/DtU6BnnfC9cJ
Right, exactly. Like: The other day I was reading about hash consing: when constructing an object, you determine if you already have a reference to it lying around, and if so you return it. The Wikipedia article about it is written with a Scheme example (as you might expect from the name? and links to the "flyweight pattern" article at the bottom, which expresses the same idea but in a very Java-brained way.
Neither Scheme nor Java have any special facilities for this, so surely if the flyweight is a "pattern" then so is hash consing.
Yeah. I I raised my eyebrows when they praised Python for first-class functions, since that wasn't really true for much of its early history. GvR was the least FP-friendly DFL out there. He originally didn't even want lambdas, iiuc.
E.g., a short list: the limitations of lambdas, the lack of readable closures until PEP 227 in 2001, and no writable closures until PEP 3104 in 2006.
The GoF book did too much damage to the design patterns community since they are NOT patterns. Patterns pre-date that book and were inspired by Christopher Alexander who wrote a books about architecture (real buildings) called "The Timeless Way of Building" and "A Pattern Language".
The GoF book took Alexander's format but lost the soul of what patterns are. Patterns are NOT an object catalog. They are a graph of solutions in context. You implement one and can decide what next to do once you do one of them based on what you need. It's a pattern LANGUAGE.
What people should read (besides Alexander's books) is Richard Gabriel's "Patterns of Software: Tales from the Software Community".
And if you REALLY want to go hard-core with understanding what patterns are, Alexander's work "Nature of Order" goes deep into what's behind patterns.
I find it rather ironic that he rather bashes Java and then praises python! I know lots of people use Python, but it's not exactly an ideal language. In many respects. Gil, for instance, it is a good article though.
Every time patterns come up, my reflex is to re-watch this presentation by Brian Marick: https://www.deconstructconf.com/2017/brian-marick-patterns-failed-why-should-we-care
Take Java, for example (I'll be bashing Java quite a bit today, so get comfortable).
It's the language that practically forced design patterns into mainstream use.
Then why was the very book about them using C++? It's almost like the author forgot about the whole OOP craze that predates Java.
(And this first year CS student-level programming language flamewar makes it very hard to take the article at face value)
This is a poorly titled blog posting. A better one would have been "Dogma Sucks. Design Patterns Being but One of Many Examples". The author admits the value of Design Patterns as creating a lingua franca to communicate with. And then goes into an unsubstantiated screed about Python vs Java.
One of the things that has baffled the software industry is how to better systemetize and homogenize programming talent. Languages, frameworks, patterns, methodologies, all of them tantalize with the hope that we can somehow flatten the differences between programmers. This still has not really happened. But every silver bullet that comes along, once it picks up momentum, becomes a dogma as economic forces encourage an industrial form of software production.
For fun, i decided to look up the 23 "original" Design Patterns, (found here https://en.wikipedia.org/wiki/Design_Patterns#Patterns_by_type) and while some are definitely niche, outmoded, or just not worth having a name for, there's still quite a few vocab words I still regularly see in real codebases.
It's also true that lots of these have been subsumed as language features, and we're better off for it: I much prefer foreach and iterators over raw numeric indexing, for example. (also in this category, decorators, templates/generics, proxies in some languages)
The other part of this, and this is obvious in hindsight: people who don't like design patterns, are often critiquing from a position of working in dynamic languages. They don't need the extra help in picking a name for their type, because they never wrote the type out in the first place! Lots of the "real" examples in old java codebases and the GoF original book are probably really extreme versions of this, with lots of helper objects to account for not having first class functions and so on, but when I need to add a name to a function signature in a modern typed language with first class functions, I'm still going to call them Factory/Observer/whatever else, even though it's a single line of code and you don't really need to think about it at all.
Design patterns are going vary to some degree from language to language. Of course Java has some design patterns that other languages might not really have, and the opposite is true.
I'm a big fan of learning and trying out patterns. Just make sure you're not using a pattern which doesn't fit the language and context you're in. That's hard to know that if you're newbie, but that's sort of the point: becoming a better programmer is knowing which patterns to reach for when!
I remember how my teacher would evaluate us based on the design patterns used in our Java final assignment :’)
It's hard to meaningfully engage with this post because it is so reductive. I'd argue it actually skirts the line of "unserious" because it sets out to be more inflammatory than expository.