Key takeaway point: "Dynamic Languages have fewer language limitations, less need for bookkeeping objects and classes, less need to get around class-restricted design. Study of the Design Patterns book: 16 of 23 patterns have qualitatively simpler implementation."
Or as someone else once put it, "design patterns" might more accurately be called something like "palliative idioms for pain points in static manifestly typed class-oriented languages."
That's a little bit unfair, given Smalltalk's place in all this, but one of the wastes of time over the last 20 years has been the ways in which some dynamic languages cargo-culted practices from, say, the Java world because Design Patterns™ were The Mark of A Professional™.
The fact this happened in JavaScript with Angular is particularly atrocious.
Your critique of design patterns is driven by the exact same thing that causes people to copy inappropriate implementations from one language to another: you both believe design patterns have something to do with the technical means instead of the design choice. The strategy pattern has nothing to do worth first class functions. It has to do with designs in which you delegate a decision to the consumer of an API. The moment I start seeing the majority of projects in dynamic languages do that instead of passing around a massive bag of options in every API call, I’ll believe that these languages have grown out of the need for specific named components of software design.
There are almost no uses of the strategy pattern that aren't equivalent to a trivial first-class function like a filter predicate or equivalent to a trivial plain-old-key-value-options-object, so the non-pattern version is stylistically preferred in the 99% case where it's not actually necessary.
Once again, this completely fails to capture what is useful about the design decision to allow user supplied strategies. Literally this week I wanted to change the retry logic in an HTTP library but I’m out of luck because it’s just a bag of options, none of which do what I want. Whether it’s a function or an object is irrelevant. We’re taking about design decisions, not code.
I submit that a filter predicate is an instance of the strategy pattern. The biggest difference between the two approaches is that one makes for a much less interesting UML diagram, so nobody ever really bothered to declare it a Design Pattern® and stick it in a long-winded book that we force college students to read.
I kind of disagree. A kind of hidden point I loved about Norvig's presentation is that there are design patterns useful and proper for Dynamic languages. The main problem is trying to use design patterns created for static languages, in dynamic languages.
I remember reading the Ruby book about design patterns. It was painful and the general motto of the book was "you don't need this and that static design pattern".
However, I think as a community we should be defining a collection of design patterns that are useful in dynamic languages. At the end of the day, the main idea behind Design Patterns was the definition of higher-level logic constructs that could be used to solve a problem when developing software.
It is like in the construction industry where bricks are the "code" and the 'best practices' (design patterns) of how to built houses with bricks do not apply when you are building a house using wood. Sure, brick design patterns do not work for wood, but that does not mean that wood building doesn't have their own patterns.
It's unfair, but also pretty decent. For my part I've often wondered if design patterns being such a big deal in OOP is really a failing of the object oriented paradigm itself, or if it's just a reflection of how, ironically enough, popular object-oriented languages tend to be really, really bad at polymorphism. You're right, Smalltalk looms large here.
Yep...there are dynamic languages (Ex: APL and Common Lisp) where you can use OO primitives, but it usually isn't as encouraged if at all. I don't recall hearing much about proper design patterns in those languages. I'm not saying they don't exist, but it isn't a big deal. Smalltalk on the other hand forces you to do OO and I just read most of a book on the proper design patterns to use in it. It certainly isn't as bad as when I started to learn Java (it's almost a minor miracle to me that as much stuff gets built in Java as it does with all the straight-jacket boilerplate it has) but it is definitely there. Especially in the scripting arena (shorter programs of maybe a page or two) I find that nearly all OO scripts I see would generally be shorter and clearer as a few global data structures and some functions to operate on it.
Funny you should mention global data structures. Last winter, when doing Advent of Code, it took me an embarrassingly long time to realize that I didn't really need to be writing super well-factored Clean Code™ with no globals or anything like that to solve these problems. And, when I stopped worrying about that stuff, my code ended up getting cleaner and easier to understand and debug.
New hypothesis: Some practices make all the sense in the world in huge enterprise projects, and no sense at all in small utility projects.
Agreed. The way I code would need to flip-flop if I was more involved with the writing of large software projects.
Funny enough though. Those that I work with still write a lot of what I would call scripts, but they're super over architected and a lot of time is wasted and it is hard to read them. Sometimes simplicity is best as you observed.
FWIW, the gang-of-four patterns book was a good idea. And still would've been, even if people were using crazy-powerful dynamic languages like Lisps instead of C++ (though the examples would've been very different, and then some would seem odd to include).
One way to look at the patterns book is that it communicated a bunch of software architectural patterns that a developer might learn over many years of reading code others had written, and writing their own code. Suddenly, a new programmer could recognize and use those patterns immediately.
Of course, at the time, it wasn't lost on the people who knew a bunch of OO analysis and design methodologies and programming languages, that there was heavy C++ bias to the exact patterns included. And we started to make comments like Norvig did here. But design patterns and the book were a good idea.
Also, I was just a kid developer at the time, but, when the book first came out, I recall one of the principal engineers was looking into how patterns might be used for documenting large, well-engineered OO systems.
The point Norvig makes is that these patterns have emerged to mitigate shortcomings/lack of expressiveness in languages like e.g. C++ and Java. Expressive, dynamic languages lack these limitations and thus there simply is no need for most of these patterns.
But some of them (we won't speak of Singleton) are patterns that are helpful in conceiving of and understanding architectures or how the implementation language is used, even if an instance of the pattern looks like a very simple expression in the implementation language?
The GoF book is full of Smalltalk examples (in addition to C++). People who claim design patterns are irrelevant to dynamic languages have clearly never read the book. It would be more accurate to say that the patterns demonstrate the limitations of OO languages than static languages.
I've mostly been exposed to the claim that design patterns are irrelevant in Common Lisp, not all dynamic languages.
Smalltalk is single inheritance, single dispatch, so it's going to need some of those patterns. I suspect that Visitor will have some considerable boiler-plate in it, compared to how it disappears in CLOS. (The boiler plate, that is, not the pattern overall; the concept of visitation is still there, just not in so many lines of code.)
Anyway, are those examples really the best Smalltalk solutions, or are they just there to show how the patterns look in Smalltalk?
For instance, the ability of an object to change type (or appear to do that somehow) will blow off those patterns that deal with simulating an object changing type as it changes state, like the State pattern.
Smalltalk has something called become which isn't exactly the same thing as Lisp's change-class, but it sounds like a reasonable facsimile thereof which could obviate the State pattern. Like if we have a widget that changes from an idle to a running state, we could just have a variable iw that is of type idle_widget, and then say "iw: become rw", where rw is a running_widget constructed from the properties in iw.
Does the book, by chance, have State done up in Smalltalk without mentioning the become operator?
> Or as someone else once put it, "design patterns" might more accurately be called something like "palliative idioms for pain points in static manifestly typed class-oriented languages."
But then one could claim that monads are a palliative idiom for pain points in function-oriented languages, with at least as much justification.
Or one could think that we're bumping into some flavor of the expression problem: There is no language style that makes all things easy to write.
> But then one could claim that monads are a palliative idiom for pain points in function-oriented languages, with at least as much justification.
Arrays are a monad. I think some monads can be described that way, but monads as a whole run into the "tensor trouble" of being so damned general that nothing can really be said about them... except for useless generalities.
The title might be confusing today: today, the title might instead say simply dynamic languages, rather than the dynamic programming that makes people think of current coding interview customs.
> This practice is not only common, but institutionalized. For example, in the OO world you hear a good deal about "patterns". I wonder if these patterns are not sometimes evidence of case (c), the human compiler, at work. When I see patterns in my programs, I consider it a sign of trouble. The shape of a program should reflect only the problem it needs to solve. Any other regularity in the code is a sign, to me at least, that I'm using abstractions that aren't powerful enough-- often that I'm generating by hand the expansions of some macro that I need to write.
For me, patterns define ways how you put language constructs units together to solve typical "higher-level" problems. For example, "how do I implement an undo mechanism". Of course for static/object-oriented languages, the patterns will be different than for dynamic languages.
I am currently listening to the Software-Engineer Radio Podcast from 2006/2007, and they talk heavily about patterns. Even though the POSA books patterns may not be applicable to dynamic languages like Ruby or JavaScript, I think the right idea is to define new patterns that are useful for these type of languages.
Maybe one reason why we don't have this is because nowadays to solve a more "specific" problem the only thing you need to do is to install some third party library from github that already does it? (like if I wanted to implement an undo-redo fucntionality I will use maybe this: https://www.npmjs.com/package/undo-redo )
Or as someone else once put it, "design patterns" might more accurately be called something like "palliative idioms for pain points in static manifestly typed class-oriented languages."
That's a little bit unfair, given Smalltalk's place in all this, but one of the wastes of time over the last 20 years has been the ways in which some dynamic languages cargo-culted practices from, say, the Java world because Design Patterns™ were The Mark of A Professional™.
The fact this happened in JavaScript with Angular is particularly atrocious.