I see design patterns books and articles as more of a bestiary than a toolkit. Their purpose is not to be a repository of prebuilt solutions (because patterns dont solve anything by themselves). Their main purpose is to give names to a good amount of very common code patterns so you can communicate about it with other programmers.
Take the visitor pattern as an example: Say we have M types, and each type has N operations. OO languages like Java are good at extending M - you add another class. But it sucks at extending N because the obvious way is to add the operation as a method in every type. Visitor pattern helps you invert the problem, make it easier to extend N while suck at extending M. In fact, the visitor pattern feels more or less like it reinvented the plain, old "function" - just write a function and handle each type in it and you just achieved the same things.
I find it's always more readable when I just use a function. There's no exhaustive type checking so it makes sense to use the visitor pattern in Java, but I often people still use the pattern in, say, TypeScript without a second thought.
What if we want to extend M and N at the same time? There's a sophisticated design pattern called object algebra . In this case, it's pretty clear that features like type class in Haskell and traits in Rust could solve the problem in straightforward ways without wrestling it with design patterns.
This take is a reflection of a failure to understand the concept of design patterns, their usefulness, and their role in software engineering.
Design patterns are not features missing from a toolkit or language. This is perhaps the biggest miscomprehension regarding design patterns. It's immaterial if a language supports or not a given programming construct in it's core language or standard library. The whole point of design patterns is that they represent higher-level programming constructs that pop up often in implementations and are frequently used to address common problems.
Think about it for a second: do futures or promises or callbacks or sharex pointers cease to be design patterns or lose any usefulness if these features are supported by the core language? Is the observer pattern not a design pattern anymore in C# or Java once it was implemented into them as first/second class citizens?
Or do we understand what these techniques involve just by mentioning these keywords?
Clearly the main problem plaguing design patterns are those who feel entitled to criticize things they don't know and clearly failed to grasp even the basics.
It's relatively popular opinion that some design patterns are used because some languages lack some features that can achieve similar stuff in "better" way.
So what's design pattern in your opinion?
For me design pattern is just approach to some specific problem, very often connected with some $implementation_example in order to communicate more effectively.
Design pattern's purpose is to improve communication by naming solutions to "common" problems.
You're confusing a couple of unrelated issues there. Just because you need to implement a design pattern if you want to use it with a language/framework that doesn't provide it's own implementation, quite obviously that does not mean that design patterns only exist if you implement them yourself. That's a terribly silly misconception, and demonstrates a misunderstanding of the very basics of what a design pattern is.
Just to be absolutely clear, even Wikipedia defines design patterns as "a general, reusable solution to a commonly occurring problem within a given context in software design", and a "description or template for how to solve a problem that can be used in many different situations." What exactly is there in the definition that ties this to an implementation?
In definition nothing, but I believe there are very popular implementations (mostly one) of given design pattern that people are aware of and associate with given design pattern
and I guess it may kinda improve communication? idk.
When you name something "factory", when some doc mentions "dependency injection", when you import a "register", when you talk with a colleague about a "decodator", if you know the name, you already know what it does, and what the API will probably look like.
I say probably, because we don't have a format definition of all design patterns, nor are they all recognized as such. For for the ones that are and we do, we don't all agree. Not to mention you must adapt them to problems and languages.
So sure, when you code and you encounter a problem, you may use a design pattern. But you don't explicitly do so. Just like a chess master doesn't chose a move by thinking "I'm going to use x", they just decide on it, and also adapt it to the situation.
I never think about design patterns when I code, only when I name the code, I write the doc, and talk about it. When I communicate.
If you read a book about design patterns, they will make no sense to you. You will probably misuse them. But if you look at real life source codes from other devs, or yours again and again, you realize some problems come back often, and a lot of people use similar solutions. Then you look it up: bingo, it has a name.
That's how it works, not the other way around. It's formalization of real life, not theory to apply.
This is an interesting analogy maybe someone who knows more about chess can correct me, but don’t chess masters have elaborate memorized openings and end games where they do decide to use “the indian defense” then go on autopilot until someone deviates from the common lines?
Just like I won't tell myself "I'm going to apply the strategy pattern here".
We just see the situation, and if it calls from a solution from the banks of solutions we have encountered in the past, we see if it fits, we try to apply it if it does (tailoring may be necessary).
Then the commentator later may say "oh, he decided to use Y". But that's for the benefits of the people watching.
if you consider the idea source -- Alexander's A Pattern Language -- the notion of a pattern is a more useful than that; it is both a communication tool, as well as the realization that the problem (in his case, buildings) can be subdivided into smaller parts, and at a certain level, these parts fall into common repeating forms (patterns). You can thus mostly initialize the design by simply composing patterns together.
The key that typically gets lost in GoF pattern discussions is that it doesn't end there. Once the patterns are decided, you need to account for the non-standard details of the problem at hand -- e.g. the house is partly on a hill. The hill raises the height of the door. The door had a relationship with the window -- it's no longer aligned, and will have to be modified to fit. With the shift of the window, the mirrors need to change.. and with the mirrors, the door. Eventually these changes will stabilize, producing an equilibrium (TB: he declares that equilibrium to be "beauty", and a feature that everyone can innately detect, though they may not know how to reach it... this corresponds strongly to "code smell" or "elegance" in programming)
So the elements of the home starts as patterns, but they're also in a fluid state, and the effects of reality will trigger a feedback loop, creating continuous design reflows, until the whole thing reaches an equilibrium (and you can now build the house).
He then goes on to note that the home exists in a continuously fluid state (as it's used and changed by inhabitants), constantly deviating from that equilibrium, and being brought back into that state. So the most beautiful homes are those not designed into a harmonious state, but maintained in a harmonious state -- I particularly like the correspondence to programming here.
Design patterns can definitely be useful, but only when used sparingly and if they really are the simplest solution to the problem. When applied just for the sake of "best practices" without really looking at the problem, all they do is create a new problem.
The other danger here is that especially the GoF patterns have often times succeeded their own usefulness with the introduction of closures, first-party functions, enums and option types to the languages. And some (like Singletons) are clearly harmful these days.
> The problem with design patterns is that it's far too easy to "apply existing solutions" to problems that don't exist.
If you use "something" (in this case design patterns) wrongly, it's not "something"'s fault. It's like saying there's something wrong with the car if you are drunk, speeding and crash it.
That's not a design patterns problem. At most, that's an abstraction layer problem/YAGNI.
> Design patterns can definitely be useful, but only when used sparingly and if they really are the simplest solution to the problem.
This statement reflects that the whole point of design patterns was completely missed. Design patterns are not pre-baked solutions. They are higher-level coding constructs that reflect specific concepts, and thus provide an ubiquitous language for developers to describe and discuss what they are doing without resorting to ad-hoc terms and definitions. Programming constructs, like callbacks, promises, futures, dependency injection, singletons, etc, don't lose their importance, usefulness or value if they are covered in a GoF-type book or if they are implemented by a library.
It's how some people tend to use them when they are not necessary or a simpler solution would have sufficed. This I believe was OP's point.
Yeah you are right, YAGNI is indeed not taken into account enough. However, blaming design patterns not only misses the whole point and shows a lack of understanding regarding what design patterns are and how/why they are used. In fact, I would go as far as claiming that complaining about design patterns is a symptom of limited technical knowledge and experience, and the bulk of the typical anti-design pattern cliches are just the defense mechanism kicking in within the spirit of the "fox and the grapes" fable.
What's the correct tool? The simplest tool that can get the job done, the Principle of Least Power. We often hear "Favor Composition over Inheritence", but never "These are the instances where Composition does not work, and Inheritence needs to be used instead." Rich Hickey's "Simple Made Easy" shows many more examples, but this principle is very wide reaching. Prefer expressions over code blocks. Prefer for over while. Prefer map over for. Prefer data over functions. Prefer functions over macros. Prefer monoliths over micro-services. Prefer unikernels over containers. Prefer smaller teams over bigger teams.
For the monolith vs micro-service, it's better to have a separate micro-service than a spaghetti monolith, but if you can un-spaghetti your monolith it will be simpler. It might not be easier. It might not be possible for your team based on the difficulty of the task. Likewise for team size, smaller is better, but only if that team size has all of the required skills. If they do not, grow the team or train your team.
The left side is simpler, and therefore composes better. If you need more power, by all means use it. If you are designing a system, always make the left easy, and the right ever so slightly annoying. Nudge people into the Pit of Success.
: I believe the answer is that composition always works if you have access to all of the code, and inheritance is a back-up if you are using a library you can't change. I might have a
You can actually see this in the article. The Python examples are awful (not very idiomatic). This is just Java written in Python, even with non-Pythonic camelCase naming.
The original Design Patterns book was written with Smalltalk in mind, which was dynamically duck typed, and had functional features (code blocks), among other things that didn't come to Java or Python for almost a decade.
Perhaps some language features are comparable to and negate the need for some design patterns, like Visitor with multiple dispatch which is provided by Julia --- but most creational, and behavioral design patterns are more just techniques that are independent of most languages and language features.
I definitely dislike the flavor of these articles that paint design patterns as a toolkit to piece together to build programs, rather than recognized components of a sound architecture which can help you build and design software that can be more easily reasoned about and extended.
It does lend to people making rube-goldbergesque monstrosities without really thinking about the why behind the way they are doing things.
I do think there are patterns I have recognized while programming in Rust, Ocaml, and a number of other languages, which never have had a description lent to them; and it's nice to see design patterns being talked about again, beyond the hype of the 1990's.
"Since we must eventually implement our designs, a design pattern also provides sample C++ and (sometimes) Smalltalk code to illustrate an implementation."
Chapter 1. Introduction. "Design Patterns: Elements of Reusable Object-Oriented Software"
The idea will probably remain popular as long as programming is a thing, with an ecosystem of blog posts, tech talks and expert books to grant it credibility.
But let me tell you this: there are principal engineers at FAANGs that wouldn't be able to name more than one design pattern (the one being Singleton).
DP might help in your career, but only if your bosses also believe in them. Many things in our profession are based purely on belief.
It's hard for me to understand this perspective when a large portion of the foundation of modern software is built on OOP and design patterns.
If you crack open the Chromium source code, you're going to see Factory mentioned hundreds of times:
As well as singleton:
And the list goes on, and on.
V8 was designed by the same engineers who built the Java Virtual Machine, and what techniques did they employ to build this massive piece of software that runs the modern web? Object oriented design, and design patterns.
It's very probable people designing modern web applications for the FAANGs are not particularly aware of design patterns, or even maybe that MVC is a design pattern even if they are using it. It's possible many design patterns aren't useful for the types of problems they are solving, or could complicate matters if they used them haphazardly.
I'm not saying this is the end all, be all, of software design. Or that "modern" functional takes on software design (most functional languages and ideas predate OOP) aren't perfectly valid, or successful. Or even that they couldn't solve many of these problems.
I'm just contesting any dogmatic rejection of what has actually been one of the most successful branches of software design.
I have to say that I agree with this. In my experience design patterns in Java lead to overly complicated, over-engineered code, think abstract factory factory factory. I've seen it and had it described as "best practice", decoupling, design patterns, etc.
I don't get blogs like these. This story has been told a million times with more or less the same content and structure.
There might be significant tradeoffs with singletons, but I wouldn't go as far as claiming they are an anti-pattern. Far from it. Managing and sharing a single instance across the whole application is a critical part of other basic software architecture constructs like dependency injection or even handing app settings.
I agree that it is an antipattern.
Not quite, you're confusing a particular way a singleton is implemented with the whole purpose of a singleton and why there's a need to restrict a class to have a single instance to begin with.
To illustrate your misconception, you can instantiate a regular class as a global variable and specify that no other instance is allowed in a project in the project's coding standards to enforce this at the code review level, and you still have a singleton.
The whole point of a singleton is that only one instance of a class is used across the system, regardless of whether this is enforced by any technice or not.
Don't confuse a specific implementation with the purpose, and requiring/expectinv only one instance of a class to be available is obviously not an anti-pattern.
I'll disagree. There are very few situations where restricting class instantiation to one object both solves a problem and doesn't cause unneeded headaches.
Almost always a boring global with careful access control is better. It provides a single object just as well without limiting options for future software engineering needs: testing, adding construction parameters, migration, gradual deprecation, etc.
As to legitimate uses, anything with system scope can't be controlled within a process. And there are few things that inherently couldn't be instantiated twice that aren't better managed by the OS.
There is no such thing as "access control to global", unless you mean humans in code review, in that case, OK
Have you actually read the definition(s) of the singleton pattern? E.g. Wikipedia: "In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one "single" instance." (https://en.wikipedia.org/wiki/Singleton_pattern)
And it's the same in most other definitions.
Also, make sure to read the criticism on the Wikipedia page - I think the criticism there is enough to classify it as an antipattern, because again, it's not about just having one instance of a type/class.
Yes, I did. I also know what a singleton is, and the whole point it's a thing. Did you read my explanation were I pointed out your misconceptions?
> Also, make sure to read the criticism on the Wikipedia page - I think the criticism there is enough to classify it as an antipattern,
You should first take your own advice into consideration and first read your sources before citing them. If you did before succumbing to your appeal to authority, you would have noticed the fact that your whole argument that singletons supposedly were an anti-pattern is what, and I quote, "it is frequently used in scenarios where it is not beneficial" because of the global state. Do you understand the problem with the way you're trying to generalize an assertion, specially given that enforcing a global state is often the whole point of using a singleton? If you fail to understand how/why/when a technique is used, that does not make it an anti-pattern.
Lastly, please get acquainted with HN's guidelines on commenting as your last comment goes against a few principles stated in them.
> Lastly, please get acquainted with HN's guidelines on commenting as your last comment goes against a few principles stated in them.
I think you are just misunderstanding me (and the OP) and I'm trying to clarify. I don't see which principle I would violate with that.
I appreciate your attempt to move the goal post and gaslight, but it's already readily apparent that you're very confident in your misunderstanding and misconceptions, and very resilient to their clarifications and corrections. You're free to learn about the basics of design patterns if you'd like but until then I see no point in continuing this discussion.
Yes, but things like this need to be repeated every few years to show it to a new generation of coders.
30-40 some year olds of today have probably been exposed to the original design patterns book already, but a younger generation grew up and worked with different tech, e.g. ruby / python / php, where these design patterns were reinvented.
I mean Ruby on Rails and some PHP frameworks from the 2000's / 2010's reinvented the MVC framework / design pattern, I had never heard of it before until these came back with it, but it's a pattern from the 70's apparently.
You should read the title as "Design Patterns for Java". You can try to implement the patterns in other languages and it may work but, if it doesn't, that doesn't mean the book is useless, it just doesn't fit your language of choice.
I believe someone would've written a book similar to "Design Patterns" at some point in time. It just happened to be the GoF.
You can say whatever you want, but today we all use patterns to talk about software. It may or may not be thanks to the book. We'll never know.
For instance I needed to traverse some directories and do something with the files and subdirs in them. So I used my parametric Visitor class.
But when I now read that code it has calls to visit() etc. and when I debug the code it never mentions 'files' and 'folders'. That has been abstracted away into parameters. And that makes it difficult to understand what the code is actually doing, what it is accomplishing.
So, I've found it gets easier if I rewrite the code to use the vocabulary of the problem domain. It still follows the same (Visitor-) "pattern". The pattern is there but code does not use the vocabulary of the pattern, but of the concrete things it is dealing with.
And maybe that's why Design Patterns are important. Not as a code-library but as explanations of "patterns" that are useful when writing software.
The reason GoF got so popular is because for anyone struggling with OO principles it was easy to understand and apply. I hope for a better way to teach design by not using patterns.
> The reason GoF got so popular is because for anyone struggling with OO principles it was easy to understand and apply.
No, not really. The true value of the GoF book, and others like it, is that it documented widely used design patterns and thus enabled an ubiquitous language to form.
I wasn't generalising about all patterns rather about using GoF patterns with a cookie cutter approach. Atleast, I am sure made that mistake.
You, the automation engineer (programmer) notice you are using the same way of solving a thing and the code of the solution at all occurrences looks somewhat similar. Well, we are no longer writing in machine code and have subroutines and functions. So you go ahead and turn the pattern into a function of your language has them. Sometimes a function is not enough, but fortunately we have macros. At least some languages do. For the unfortunate ones with less powerful languages you have to keep it at the pattern stage and never evolve to reduce the boilerplate or automate your job away.
For this reason design patterns are often called the "missing language features". Which you can never understand until you have touched the powerful languages with metaprogramming capabilities.
Now, there are situations in which design patterns have to be used because they are applied at a cross section between systems. There is no single language encompassing your entire code, but many languages for many systems. Unless you go crazy with string templating (ie. inventing a new poor man's language), you will not be able to move beyond the pattern stage. On the other hand, a repeated instance of the problem between systems suggests there could exist a language that many people would use. The problem was repeated after all.
This usually gets resolved by a simple system integration engine, which is operated through some json, xml or yaml configuration file. Eventually there comes a need to introduce variables, file includes, loops, etc.
People turn to become 'advanced yaml engineers', like now in the k8s world, for what perhaps could be managed by programming language.
After many frustrations, eventually a new special purpose language will be built for it.
Long lasting design patterns in the codebase are pointing at missing language features. Or at a new programming language at the horizon.
It would help if the design patterns could be defined per language and written in the language's own idioms.
This completely misses the point. You don't "pick a pattern" and choose to apply it.
Design patterns are a way to take your best solution and describe how it works to other programmers.
For example you might say something like: "We used a singleton here because we can barely manage to get one instance working correctly and never intend to write tests".
No wonder it is so beloved by all those enterprise architects - it allows to "architect" without actually architecting, like building a bridge without an overall bridge plan and calculations, only with the plans of its components and just piling, "injecting" and smashing these components together.