16 of 23 patterns are either invisible or simpler, due to [...]
When it does have the expressivity, the underlying rendering of the pattern hasn't gone away; just the manual boiler-plate for producing it.
For instance, if you're working in assembly language, then you might sometimes benefit from a "while loop design pattern" to keep your code clean. The while loop design pattern calls for some test code before a block which jumps past the block when the test is negative, and an unconditional backward branch at the end of the block back to that test expression.
The while loop doesn't go away when you use a higher level language. You just indicate that you would like that pattern, and the compiler spits it out for you, invisibly.
> Every time you pass a lambda to a higher order function you're using a strategy pattern.
Every time you pass a "naked" lambda somewhere, you're potentially missing the opportunity to have a macro there do that for you.
True but equally sad that you must use Lisp to hide ugly Lisp because naked lambda's are so ugly. Yet the point remains, whether hidden behind a macro or not, design patterns still exist in Lisp and every other language and always will.
The point isn't to hide the lambdas, but to hide the "how", which might or might not use lambdas.
The "how" could instead open-code the procedure that would have otherwise called the lambdas; then the material just becomes embedded forms in the inline code.
Hiding lambdas with macros isn't really that common in my experience. The "how" being hidden is much more often (ultimately) IF, LET, BLOCK, and GO. Of course, higher order functions could often be used instead, but they're generally not.
Because it is mine, for better or worse. It took me from understanding OO to understanding how to build large systems with OO, writing maintainable code and using proper encapsulation. A much deeper work than Code Complete (though the latter is worth reading too).
I have used functional languages as well (ML, Lisp; not Haskell yet though I think I get what monads are; plus I write a lot of Python, C++, JS in functional style) but I have yet to use them for any project as big. I hope I get the chance to one day. But for now Design Patterns gets my vote.
Apparently so; sad.
http://www.perlmonks.org/?node_id=133399 does a very good job of explaining why Design Patterns is a book to be careful with. By contrast Code Complete won't steer people wrong.
See https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo... for a cautionary tale about how a pursuit of OO purity can result in great verbosity, hiding intent behind a barrage of patterns. Resulting in code that I, for one, would rather not work with.
But then again I'm hardly enthusiastic about OO. See http://www.perlmonks.org/?node_id=318257 for more on my perspective.
DP are oft misused, but I think the main idea is a pattern language - a way to communicate to other developers what the architecture is. This is especially important on larger, more complex projects with more developers and developer turnover. Understanding the architecture of a project is crucial for grokking it, but usually not well documented. Even just "DPs are naming conventions" is helpful in this regard.
I really disagree with the idea that most DPs are particularly clever or helpful - they are just the way you'd end up solving those kinds of problems yourself. If you look at your own pre-DP code, you'll see design patterns. What they give is a standardized terminology ("pattern language").
Sometimes an architecture seems more complex than necessary, but makes sense as you better understand the whole problem. e.g. how curl handles http. e.g. youtube-dl. (for me, when I reverse-engineered mini-versions of them, then studied their source)
tl;dr "DPs are docs"
1. Being able to use functional programming concepts in OO languages made a lot of design patterns irrelevant (Python, Ruby, C# 3.5, C++11, Java 8). I feel Crockford's The Good Parts really introduced these concepts to the OO crowd with javascript acting as the bridging language for the concepts.
2. Many design patterns were terrible ideas in practice. Factory patterns were a disaster, as were many of the others. Good in theory, utterly useless in practice. There was a blog here recently about TDD that argued TDD is useless because you need to be a great architect to do TDD correctly, and most programmers aren't great architects. Design patterns had the same problem, it was very easy to get them wrong and create an utter mess. Ultimately it's more maintainable to have a complicated monolith method than some crazy pattern no-one understands or dare touch.
Those languages are higher-level languages. A considerable number of design patterns disappears in them already.
In Java or C++, you have functions with parameters and local variables.
In assembly language, there is a "design pattern" of pushing words onto a stack to generate arguments, and moving the stack pointer to reserve a frame for local variables, and then unwinding these actions when the function returns. Also, saving certain registers and restoring them may be part of the pattern. This pattern can be supported with macros and pseudo-ops to varying degrees.
In these higher level language, this pattern disappears. The concept of parameters and local variables doesn't disappear, of course. Those things are just declared in a simple way.
The assembly language pattern for calling conventions is in fact a back-formation from higher level languages. It won't even occur to assembly language programmers until they think of their programs at a higher level, and translate from that to machine code.
Or: let's look at C++ vs C. In C++, there are declarative mechanisms which generate code that can be understood in terms of C. It captures coding patterns, such as ensuring that a structure is properly initialized when instantiated, and cleaned up on every exit path from a function.
Implementation-recipe design patterns only need to exist (or, rather, only need to be part of the active, day-to-day awareness of more than a small number of programmers per language) when the target language does not support implementing the common solutions as reusable code modules because doing so would require a kind of abstraction the language does not support.
This has nothing to do with the OOP paradigm particularly; there's no reason OOP languages can't support powerful abstractions that reduce the need for that type of design patterns; the GoF book just emerged when the most popular industrial languages were both OOP and lacking in those facilities.
- In Java [2] or C++, to implement that pattern, you may have to define an abstract class, or a class with some abstract and some concrete methods, and subclass it, and in the subclass, redefine some of the abstract methods (of the superclass) concretely. (Maybe interfaces can be used here instead of abstract classes/methods - in Java. Haven't used Java for a while, so not sure right now.)
- In Python, you can just do:
def transform(input, output):
# Code here to read data from input, transform it, and
# write the transformed data to output.
Now let's say the code in the transform function does input.read() and output.write() (with maybe some way to indicate when the end of input is reached, like the null string is used in Python). In other words, methods like those of Python file objects are used for reading and writing. Given all this, you can now pass objects of any types as arguments for the input and output parameters, as long as they implement a read and a write method respectively, and the code will just work.This has been described as "smooth seamless polymorphism" in Python. It's also known as duck typing.
https://en.wikipedia.org/wiki/Duck_typing
[1] The Template Method Pattern is one of the ways in which frameworks (as opposed to libraries) are implemented. The abstract methods of the framework are implemented concretely by your code that the framework then calls, using the Hollywood principle - Don't call me, I'll call you, a.k.a. Inversion of Control (IoC).
https://en.wikipedia.org/wiki/Template_method_pattern
https://en.wikipedia.org/wiki/Inversion_of_control
[2] Note that what I wrote is based on knowledge of older versions of Java - newer versions may have features that make the pattern simpler to implement, maybe without abstract classes/methods.
The Command pattern, in its most basic form, could also be considered as implementing 1st class functions by wrapping them into objects.
So if you're using a functional langage, there's a good chance you're wondering why consider both of the above as "design patterns", as you're seing them as basic things, and the compiler implements them for you.
The visitor pattern addresses doing multiple dispatch in a single dispatch language.
> The Command pattern, in its most basic form, could also be considered as implementing 1st class functions by wrapping them into objects.
BTW, that's not really true; first class functions are not a replacement for commands. Commands are about turning behavior into state so it can be stored and executed, potentially replayed for example, at a later time. Commands aren't just functions, even languages that have first class functions still use the OO command pattern when doing things that benefit from command patterns.
First-class functions plus partial application do that trivially. (First-class functions provide the ability to store a generic command, partial application let's you get a specific instance with local state that can be “replayed” against a different external state object.)
Many times commands come with multiple actions, to allow execution and rollback, require being able to be serialized to disk to database and replayed later, or are used to better organize and plug in extension commands to large code bases. None of these cases are amenable to first class functions.
The command pattern isn't just a hack around not having first class functions, we still use command even when first class functions are available. You aren't going to extend Photoshop with a new filter using a first class function; you're going to write a plugin which is a command pattern for filter execution.
Stop trying to tell me what first class functions can do, I'm fully aware, I use them every day and have for nearly 20 years. You aren't talking to some kid who never heard of functional programming.
> BTW, that's not really true; first class functions are not a replacement for commands.
Please note that you're extrapolating from my post: this isn't what I said.
We still need this pattern: there's no way you're going to implement "undo" using only first-class functions, and I'm well aware of that.
In some specific situations, though, we don't need it anymore, like when connecting a GUI "save" button to a "document.save" function.
False. Lisp has macro's and can abstract anything syntactically, yet still has commonly recurring design patterns such as with-X macros. You misunderstand what a design pattern is if you think new language features can remove the need for them.