Isn't this called 'dynamic programming'? It's actually a habit people should pick up when grinding leetcode.
Just like we have words for a nail, a screw, a hammer. If you had to say "I used a tool that I find works well with nails", that would be annoying and ambiguous.
Now of course, if you quickly screwed something with your swiss-army knife and a junior came and told you that this is wrong, because you should always use a proper screwdriver, and therefore you should rent a car and drive 30min to go buy it right now, you would kindly tell them to fuck off. Doesn't mean that there is no value in the concept of a screwdriver itself.
The thought model complicates the process of writing new code, "what pattern do i need here?" and the perception of existing code, "oh this is pattern X, why isn't that communicated clearly?". The truth is that design patterns are at best stagnant in quality and quantity over time (GoF is over 30 years old!), but the quantity and quality of problems is infinite.
I've thought in patterns quite some time in my early career. With my colleague back then everything was an pattern and we were kind of in sync with that approach. But it quickly falls apart if you have peers that are less try hard on it. You can spend days, weeks, months to study design patterns and then have to explain it to someone in 5 minutes that simply doesn't care. I can't blame anyone to not care, so that has to be accounted for.
I think the common language argument is tempting, but also too stressed. Good and useful programming knowledge is bound by reality. An integer is a indisputable thing that is always useful. A "singleton" is just a fancy rephrasing of "global state".
Say I build a tool that makes it easier for me to drive a nail and call it a "Naildriver". If I show it to a colleague, they may be able to tell me "oh, this is generally called a hammer!". Maybe they will even tell me how I may improve my hammer, because they happen to like to learn about hammers in their free time. Or maybe now that they said it's a known concept, I will enjoy reading about hammers online (doesn't mean I will now read the entire encyclopedia of tools).
The fact that there is a name for the concept ("it's called a hammer") does not say you have to know the word. It's just useful to have a word for it, because then we can reference it in discussions and share knowledge about it more easily.
To keep in your analogy, if you have a 3KG naildriver with a 2m handle you'll quickly find out that driving nails into a drywall with that leaves you with no wall. And that it's a bad idea to use a "Naildriver" to drive these "spiralled-slothead-nails" (aka screws) into wood.
But with a common language, such as design patterns, you can easily learn where they are good, what their limits are, in what cases other patterns fit better, and what pitfalls to avoid and so on.
If I search the web for "why is it so hard for my naildriver to drive in spiralled-slothead-nails" I'll get zero results. But when I search for "why is it hard to hammer in a screw" I'll get good results. May sound like a silly example, but for me that sillyness illustrates how obvious designpatters should be.
And it doesn't mean at all that everybody should learn the whole encyclopedia of tools by heart. But having it formalised somewhere is useful: as soon as someone tells me "what you're trying to do sounds like this design pattern", I can start searching and reading about it.
Of course if that someone tells me "you suck, you should know that there is a design pattern for that because you should read about design patterns every night before you go to sleep", it will be different :-).
I think Julian Assange once said he would refer to things in discussion just as "Tomato" (or similar), in discussion to have a shortcut for something unnamed with some meaning. We do this all day in programming, we give a complex component a name and it means a lot more then just the actual word. The problem is that this specific meaning is often not universal, it's contextual.
If you take a hammer, an integer and design patterns, the latter is the least universal thing. They depend on domain, context and conventions, and can change quite a lot depending on the language. Removing hammers and integers from the world would cause collapse, removing design patterns would do almost nothing.
I guess the active celebration of design patterns as a catalogue of general wisdom is my primary issue here. I'd welcome any project/team/company that maintains a catalogue of their individual "patterns". But a universal source of truth has too much bible flavor to me. (Oh it also dictates OOP by default which renders it's inherent "wisdom" completely useless in many languages)
But formalising concepts with words makes sense. If your company maintains a catalogue of their patterns, and someone happens to know that this specific pattern is usually called a "singleton", I would find it weird to call it a tomato.
Some patterns have different names in different contexts or languages, and that's fine. I don't find it weird to have a discussion around "in this language there is this pattern that they call X, does that ring a bell for you working on that other language? The idea is [...]", and maybe the answer is "yep we have it too" or "oh, we call that Y, but that's pretty similar".
I've had some debates with junior devs who really wanted to enforce their newly-learned patterns, and my experience there is that passed a certain point, there is no convincing anymore: if they really insist about overengineering something with their newly-learned concept and the hierarchy doesn't allow me to prevent them from doing it, anyway they will do it. So instead of fighting I just let them, and if it turns out to be a waste of time in the end... well that's not my problem: I was not in a position to prevent it in the first place.
It's not only patterns though: some juniors have a way to try to use every exciting framework or library or tool they just discovered.
Probably a bad habit trying to stop them, they need to learn walking before they can sprint. The question is always how to put limits on them? I say it's overengineered, they say it's modern code and I am old. So what do you do if they send you a PR that is just plain wrong? I mean it seems like you delay the conflict from upfront design/planning to pull requests.
* Functional teams (established companies): enough seniors are here to tell the juniors when they are wrong, and the juniors naturally accept the criticism because it's expected in that environment.
* Dysfunctional teams (startups): most devs are juniors anyway, there is no clear hierarchy, managers are still at their first job so they have never seen a functioning team or had a competent manager themselves.
In the second case, there was absolutely no way I could win an argument: the "high energy kids with big egos" never believed me, I was the "old guy" as you mention. I remember an example where the "kid" failed and gave up with their "right" way after 3 months and I solved it in 3 days, exactly how I had suggested they did it in the first place. Next discussion, nothing had changed, they still knew better.
I can't really blame them, because the whole environment was like this. Everybody was always right, knew better, etc, even though it was their first job.
What I've learned is to protect myself and move away from that. Either by changing job, or by changing project. In startups I have been pretty successful at saying "I can help with this project if we do it this way, but if we do it that way, I can't help and I will work on something else". Of course it meant that I could not work on the most exciting project, but they made a mess out of them, so all in all I feel it was better working on my boring, stable, maintainable components.
I think once you have spent enough time in a software space, nothing is really new under the sun. That's why I think the GoF has aged well (controversial opinion I know!).
Or some tool makes it easier to write code safely, but in doing so, it encourages people to architect their code in a certain over-complicated way and it ends up being a net negative.
But anyway, this site seems javascript/frontend wanna-bees oriented.. please don't take those tutorials as mantras-to-follow-at-any-rate. See if you can take the knowledge and move on.
A very good book, besides the GoF one, is the "Organisational patterns book by James Coplien, Neil Harrison" [1]. It contains some of the GoF *plus* all the non-technical ones, and they are bundled together - as software making is not just the coding.. i have the list of those patterns essences (patlets) extracted, here the link - https://www.svilendobrev.com/rabota/orgpat/OrgPatterns-patle...
edit: it's from ~2003-5 and images are missing. May need to scan them from the book. Closest i found is [2], at least to get some idea
[1] http://www.amazon.com/exec/obidos/tg/detail/-/0131467409/
[2] https://www.scrumbook.org/book-outline/history-of-the-patter...
We need to keep in mind, that the most composable concept in computer programming is the pure function (or maybe a mathematical "relation", if we want to abstract further). Not a mutating object. Not some constellation of objects that constitutes a pattern. Those are merely special cases.
I am currently developing a GUI application. Most of the classes in my code are classes, that are custom widgets. The GUI framework is OOP, so that is kind of infectious for the rest of the code. Still I try to keep some stuff separately as pure functions. Probably will outsource more stuff like that into completely self sufficient functions. The only pattern I have so far in the whole application, is a mediator, which I use as a way to have objects register themselves as listeners to specific events and for other objects to send these events to anyone who will listen. That way objects don't need to know, which other objects are interested in learning about some event. Could I have built in some factories and whatnot? Surely I could have, but there would have been very little, if any benefit. Even the mediator only exists, because the GUI framework does not have a way to send events that include data, so I need a way to do that. Otherwise I could even get rid of that pattern as well.
In this way pattern are useful for when you really need them, but these OOP pattern by themselves, without context, don't really have a value. No code becomes better by building in more patterns, unless the situation requires such a solution.
There are a surprising number of topics; not only in engineering, but in day-to-day life as well, that we tend to think we all understand and have consensus about, but in reality nobody agrees on the same thing and there is no real consensus.
People tend to form consensus over labels; not over concepts. They may fully agree over labels but actually fundamentally disagree over the underlying concepts; they only realize this once they start defining their interpretation of the terms.
Even some principles aren't known. I always recommend the book Head First: Design Patterns. It's in Java, but the lessons can be applied in every language.
Unfortunately, we are in a 'post-knowledge' era... I don't know how we can keep things up at this pace.
I can only discourage anyone from applying Java patterns all over the place. One example in JavaScript: There was a functionality that required some parameters with default values. The plain solution would have been:
function doStuff({ x = 9, y = 10 } = {}) { ... }
Instead, they created a class with private properties and used the builder pattern to set them. Totally unnecessary.Design patterns cannot be applied in every language. While some patterns are applicable everywhere, many of them provide replacements for missing language features. For example, the Builder pattern is not very useful in languages with default parameters and named arguments.
Builder is extremely useful to pair with a parser, e.g. SAX. The parser parses the input, and the builder then decides what to do with it.
Let’s remove the category argument and you get this:
Post post = new Post.Builder()
.title("Java Builder Pattern")
.text("Explaining how to implement the Builder Pattern in Java")
.build();
This builder is a more readable alternative to this: Post post = new Post("Java Builder Pattern", "Explaining how to implement the Builder Pattern in Java", null);
But if Java supported named arguments and default values (the default would be null), this could just be: Post post = new Post(title: "Java Builder Pattern", text: "Explaining how to implement the Builder Pattern in Java");It definitely biases how people evaluate llms. Many cite Claude as their favorite llm for generating frontend code, but I suspect that many people prefer it because the output is prettier, rather than better composed.
In a world with only assembly language, for instance, it’s a bit like making a big deal about a “guarded repetition” pattern (aka a while loop).
Eg in Lisps, a lot of patterns become one-liners. At that point these patterns become a “can you write decent Lisp” question[1].
There are two cases that are unique though: state machines and visitors are so much things on their own, that you'll use those names almost every time you run into the pattern.
Yes, but what about factory factories? https://factoryfactoryfactory.net
The magical "visitor" pattern becomes nothing more than simple callback passing or passing a function, which is one of the most natural things to do in a modern programming language.
State machines at least are useful as a conceptual thing, to find a way to think about how one solves a problem, no matter how they are ultimately implemented in the end.
Fahrenheit toFahrenheit(Temperature t)
{
return Fahrenheit(
t.match!(
(Fahrenheit f) => f.degrees,
(Celsius c) => c.degrees * 9.0/5 + 32,
(Kelvin k) => k.degrees * 9.0/5 - 459.4
)
);
}I have to touch some of the most unnerving modules in a legacy project and everything is a trap. Lot's of similar repeated code with ugly patterns and some big brain trying to hide the ugliness with layers and layers of indirections and inheritance, and calling it clean because there is a factory class. The biggest joke? each implementation have a different interface for key methods, so later you to check what instance got created.
I want to keel myself. Anyone could assist me in a seppuku?
This is a "hash join" in relational databases. You can see it in the query planner output of at least postgres.
This is an instance of “use the right data structure for the job”. To me it has little to do with architectural design (where design patterns live), but it has to do with algorithmic design.
To talk about design patterns as not as useful, only to then mention big O notation seems strange to me unless you are in a context where performance is critical?
Worrying about O(n) IME is far less important than choosing the right architectural pattern. O(n) issues are usually observable via metrics or QA and are typically straightforward to fix. By contrast, recognising that your pattern choice is wrong is harder (since it manifests during dev rather than in prod) and takes more effort to rectify.
I also disagree that patterns don't deserve a name. I have found it very useful when discussing with both seniors and juniors to have a common name for a pattern being described. Seniors known instantly and it can be helpful to have a resource to point juniors to if they aren't familiar. I have also found it useful when English isn't the first language.
I do agree that seniors don't typically try to fit standard patterns to their problem in a way that a junior might, that's a fair point.
Juniors often think that learning design patterns is some kind of career hack which will allow them to skip ahead a decade of experience... There is some utility in many design patterns but the problem is that juniors often miss the nuance and the motivation behind the patterns and they tend to misuse them; often creating more complexity than they would have created had they not used design patterns.
There are situations where design patterns may be useful but they're much more rare than most people think. A lot of juniors seem to think that every problem requires them to apply a design pattern from their toolbox. They try to frame every problem within the constraints of design patterns that they know. This can create problems.
In fact, there are many coding patterns which are far simpler than 'design patterns' and much more useful, but nobody talks about them because they're too trivial to discuss. For example, I've seen people write code which relies heavily on design patterns but then that same code uses an O(n^2) nested loop to find items that are common between two arrays. There is a simple 'pattern' you can use to store the items of the first array in a Set or HashMap and then finding common items is O(n) because Set and HashMap lookups are O(1)... Very useful pattern but I don't believe it has a name. I use it literally ALL the time. The idea of storing intermediate state in some kind of HashMap is a game-changer IMO but there's no name for that pattern of coding. In general, knowing what data structure is most appropriate for various scenarios is a game changer... But still, don't abuse. Basic data structures like arrays are fine most of the time.
Anyway, it's good to absorb the wisdom behind some design patterns but you should not try to fit every problem to them and dont say stuff like "For this problem, I applied Design Pattern X" - If you do, senior engineers will know you're a junior. If you use a design pattern correctly, it will probably not look exactly like in the textbook. It's kind of hard to give it a name. It may be a mix of patterns. It's the principle that counts. Reality is too complex for rigid design patterns.
On the other hand, some design patterns are too common and obvious to deserve a name. For example, the factory pattern is super common... I use it all the time but it's so basic and obvious that I will sound like a total noob if I go around calling my function "socket factory pattern"... I will call it "Utility function to obtain a socket" or something. I will never refer to it as a factory pattern, it's a bit cringe.