Remix.run Logo
emaro 2 days ago

Design patterns can be really helpful. In my previous job I worked on enterprise .NET applications. It made sense to use common patterns, because most applications were big and the patterns made it easier to understand unfamiliar code, within an application but also across different teams and applications. New projects looked familiar, because the same style and the same patterns were used.

Now I'm working on an old (+10 years) JS application. Similar patterns were implemented, but in this case it's not helpful at all. The code looks very corporate and Java EE style, with a ton of getters and setters (`getName() {}`, not `get name() {}`, factories, facades, adapters, etc, etc. It's usually completely unclear what the benefit of the pattern is, and code is more complicated, for instance because creating new instances of business objects is split into `Object.build` which calls `new Object`, with no guidelines at all what part of the initialization should be in `build` and what should be in the constructor.

The gist of my comment is that patterns can be useful, but usually they're overused and if you implement one without understanding why and without benefiting from faster understanding the code because the pattern is applied consistently over multiple instances, the result is worse than just implementing what you need in a readable way (YAGNI).

layer8 2 days ago | parent | next [-]

I would put it slightly differently: Patterns (including anti-patterns) happen whether you call them such or not. Any developer will sooner or later come up with patterns like Adapter or Builder or Composite or Iterator. In that sense, patterns are not invented, but discovered. The benefit of design patterns is to be able to communicate these discovered patterns, and to define agreed names for them, so that you don't have to describe the pattern each time you talk to another developer, but can refer to it by a well-understood name. (Or when not yet well-understood, can refer to the corresponding pattern description.) It extends the language we use to talk about software design.

The point of design patterns is less about the individual patterns, than about having "design pattern" as a general concept of coding patterns relevant to software design, that you name and describe because they keep reoccurring.

zeroq 2 days ago | parent | next [-]

I would go even further.

For me design patterns are more of vocabulary than a tool.

It's not about - hey I found this book and we'll be using these building block from now on - rather, it's about having words that everyone immediately recognizes and associate with exact same ideas.

naasking 2 days ago | parent | prev [-]

Yes and no. Some patterns exist because the language isn't expressive enough. This is one reason why the patterns made sense in the OP's .NET programs, but made less sense in JS. JS simply doesn't require as much ceremony for some things because it's dynamically typed and reflection kind of comes for free.

moron4hire 2 days ago | parent [-]

I would say that reflection in JS is terrible compared to .NET. You can only just barely figure out what is in an object, but it's a hell of a time figuring out what any of those things can do. I wouldn't so much as call what JS does "reflection" any more than "making objects out of poorly implemented hashmaps."

no_wizard 2 days ago | parent | next [-]

>You can only just barely figure out what is in an object

There's a couple really well documented and understood ways of doing this in the language. I'm not sure what you're specifically referencing without more information.

>I wouldn't so much as call what JS does "reflection" any more than "making objects out of poorly implemented hashmaps."

Is this anymore different than .NET deriving everything from a base `System.Object`[0] type?

Also, what is missing in JS reflection wise that you can't do that would make sense for its environment? (namely, this excludes compile time reflection stuff I know .NET can do, it wouldn't make sense for a scripting language as it currently is)

[0]: https://learn.microsoft.com/en-us/dotnet/api/system.object?v...

moron4hire 2 days ago | parent [-]

In JavaScript, one can tell if an object has a method by iterating over the object keys and seeing if the value is `instanceof Function`.

But that actually tells you very little. You might be able to tell that it takes a certain number of parameters, if you are running on a system that implements Function.prototype.length. But you will have no way of telling what the arguments to those parameters should be, or even what they were even named. There's no way to tell if the function is a method that needs to be `.call()`ed with a value for "this", or if it's just a function that happens to live in an object literal, or if it's actually a class constructor that must be called with `new`! And there is certainly no way to tell whether the function returns a value, say nothing about the type of value it returns.

With .NET reflection, I can do ask those things I lament missing in JS, and guarantee the type safeness of it.

matheusmoreira 2 days ago | parent [-]

Isn't this just a fundamental limitation of dynamic typing?

moron4hire 2 days ago | parent [-]

Most of it is not. Most of the data necessary to support reflection should be available to the runtime or else it wouldn't be able to operate. The runtime is parsing the syntax of the function, it should be able to tell if all exit conditions have a return of any kind. It should be able to tell me at least the names of the parameters. It should know if a function is bound to a "this", or of it's a constructor. It just doesn't give any way to tell me.

naasking a day ago | parent | prev | next [-]

> I wouldn't so much as call what JS does "reflection" any more than "making objects out of poorly implemented hashmaps."

I used "reflection" because that's how it's abstracted in popular statically typed languages. My point was that JS has abstractions and idioms that eliminate some of the need for design patterns like, factory, decorator, strategy, etc., and some of the reasons are because JS's core objects are basically just fancy hashmaps.

actionfromafar 2 days ago | parent | prev [-]

"Javascript" === "Chaotic neutral lisp"

socalgal2 2 days ago | parent | next [-]

lisp to me is (1) the language itself is a lists of lists (2) defmacro lets you manipulate those lists of list at compile time. JS doesn't this do either of these at all AFAICT and so is absolutely nothing like lisp.

Most lisp programs are about writing DSLs using defmacro.

What's the similarity to lisp except that both are programming languages?

actionfromafar 2 days ago | parent [-]

Let me ask you instead, do you consider there to exist any Lisp which has no compiler?

moron4hire 2 days ago | parent | prev [-]

Yeah, I don't like the comparisons of JS to Lisp, because I think they mostly center on the existance of the map and filter methods of Array. To me, that's just not what Lisp is about. C# has map/filter/etc, and we don't say C# is-a Lisp.

And there are many other such features that were once unique/unique-ish to Lisp, that were major selling points for using Lisp at the time, but are now pretty common across a very diverse set of languages. Garbage collection being one. Higher order functions being another.

Lisp's big idea that stuck to being unique to Lisp is homoiconicity. It's the one thing that continues to be valuable enough to warrant using Lisp, despite everything else that has been stolen and copied.

Of course, not that I ever used Common Lisp, and not that I use Racket anymore. I enjoyed the hell out of programming in Racket. Up until the point I needed to access a database. Man, who's got time for that Jankasarous Rex? But I really would love a homoiconic language for the .NET CLR. That would be pretty sweet.

actionfromafar 2 days ago | parent [-]

Well put.

skydhash 2 days ago | parent | prev | next [-]

A lot of patterns only make sense in languages like C# or Java, which are inflexible by design. You have two hierarchical trees (inheritance and namespaces) that you have to work around. With something simpler like C, Go, JavaScript, you don’t have those obstacles and a solution can be way simpler to implement.

ssrc 2 days ago | parent | next [-]

Some patterns in the GoF book only apply to C++/Java as they were in 1994, but I don't see any reason why other languages would have no useful patterns. The Linux kernel (C) is full of patterns for example.

Funny thing, Peter Norvig also has this position, that patterns only apply to languages like Java, but his book on Lisp and the Python course he had on Udemy (?) are super-pattern-y.

liampulles a day ago | parent | prev | next [-]

I went from Java to Go for my last job. I think there is a reduction in pattern stuff but there are still many (at least in my project).

Java is kind of "begging" for patterns IMO, with all its static package protected final stuff and beliefs around encapsulation. Go is plainer, which is to its benefit.

mrsmrtss 2 days ago | parent | prev | next [-]

How do optional inheritance and namespaces (which you can ignore to use a single global namespace) make a language inflexible? If anything, these traits make your language more powerful, not less.

richardlblair 2 days ago | parent | prev [-]

Somewhat true - I usually find that in these languages the patterns are there they are just less obvious.

jonkoops 2 days ago | parent | prev | next [-]

This is a common thing I see when developers that come from an OOP enterprise environment familiar with Java, C#, etc. do JavaScript, they try to use all the same patterns, default to classes for everything. It just doesn't fit the language.

threetonesun 2 days ago | parent [-]

It was fascinating to me to see JavaScript add the class keyword, have it be widely adopted thanks to React, then just a few years later seeing `class` in JavaScript code is, as you said, a clear sign a Java/C# dev was here.

arscan 2 days ago | parent [-]

I haven’t done JavaScript in a long while, is using ‘class’ not a favored way of writing JS these days? I wrote JS heavily pre-class, and never really got comfortable using it before switching my focus to other languages.

gcau 2 days ago | parent [-]

The poster you're replying to is plain wrong, using "class" is ubiquitously common in the javascript/typescript world, it's the idiomatic way to create classes, and it has better semantics than trying to use prototypes. You might compile away the class keyword for compatibility, though.

kbolino 2 days ago | parent | next [-]

But you don't have to do either of those things. There's a third way, with functions and bare objects. I'm not sure that's what GP meant, but a lot of the JS I've written (which tends to be for the browser, mostly vanilla, and quick-and-dirty, to be fair) never touches classes or prototypes. The JSON data being produced/consumed is just a bag of fields, the operations on the document are just top-level functions, events get handled in callback closures, responses to HTTP requests get handled with promises, etc. Sprinkle in some JSDoc comments and you even get fairly workable autocomplete suggestions. Of course, the web APIs are built on prototypes/classes, so it's not like they're totally absent. But with things like data attributes, querySelector, and HTML templates, the usual needs for my own code to be OOP (or even structs-with-methods a la Go/Rust) just don't emerge that much.

arscan 2 days ago | parent [-]

Yeah, I would do a lot with plain objects, and using closures and iifes to do encapsulation and what-not. It was ugly and a bit of a pain, but once you learned how it all worked it made sense and was doable. I felt that classes were a bit of a bolt-on that violated my own internal understanding of how JavaScript worked, but by that point I was moving on to other stuff so never really got used to it.

threetonesun 2 days ago | parent | prev | next [-]

I'm not denying the existence of class in JavaScript, but at least from what I've seen when React went to functions so did most of the JavaScript community that had moved to class based syntax, except for those who worked with Java/C# as well.

thunderfork 2 days ago | parent | prev [-]

I think the real sign of this is a class where all the members are static, or pure data classes - ie, classes as a default rather than classes for things where classes make sense

ozim 2 days ago | parent | prev | next [-]

I see a lot of comments about how patterns are useless from people writing toy apps or at least ones that never had to deal with really enterprise scale stuff. So for me they are like people building a shed screaming at people building sky scrapers that no one ever needs to pour so much concrete to form a foundation.

Parent comment is not like that.

daxfohl 2 days ago | parent | prev | next [-]

Yeah, I've seen (okay, and been responsible for) a lot of "the road to hell is paved with good intentions" due to jumping to some pattern or other because, well, it feels like the right thing to do. It makes the code cleaner at delivery time, and is usually very intuitive when the design is fresh in your mind. But IME it doesn't take long before that freshness goes away, and the next time you look at it (let alone anyone else) you find it hard to follow the logic anymore. Usually "constructing" the pattern and "executing" the pattern are in different places in the code, and there's not a straightforward way to mentally step through one without continually cross-referencing the other. At some point you wish you'd just written one long switch block that you could step through.

And that's all before new requirements that break the pattern, or other engineers that don't have time to grok the design and hack in something that looks like a switch block for their thing, which eventually takes over most of the code anyway.

khannn 2 days ago | parent | prev | next [-]

People were shifted from Java to Javascript and kept the Java patterns and maybe the organization had standards requiring their use.

baq 2 days ago | parent | prev [-]

design patterns are a language, it's just that the programming language they're being implemented in doesn't support them natively.

e.g. observer pattern in java is what, [array of functions].forEach() in js? not worth calling that by name. another example, singletons - in Python, it's just a module (caveats apply obviously, but if we apply them, some also apply in java).

this is why designing a minimal language to make it 'simple' is misguided: you'll end up having to reinvent the design pattern language anyway. there are good reasons to design a simple language, but simple for the sake of simple is missing the point.