DEV Community 👩‍💻👨‍💻

Discussion on: You don't need classes

Collapse
 
michi profile image
Michael Z • Edited on

It's more about code organisation more than anything else for me. I rather have one class be responsible for something than 10 loose functions all transforming a piece of data slightly differently.

One use case I like to use classes for is ORMs like in Adonisjs:

const user = await User.find(userId)
user.name = 'new name'
await user.save() // user.delete(), etc.

// Or

const user = new User
user.name = 'new name'
await user.save()

// Or

await user.projects().create({ name: 'new project' })
Enter fullscreen mode Exit fullscreen mode

All the above just reads very natural to me, the API is very pleasing and simple.

But I agree that they are not needed in many cases.

Collapse
 
raulinlee profile image
Lee Raulin

Just for code organization... Well guess what, there is this super-cool new cutting-edge feature called "files", which can be organized into these things called "directories". It's amazing. :D

Sorry if I sound smug or whatever... It was genuinely a lightbulb moment for me when I realized that the "revealing module pattern" was functionally equivalent to a class is many ways... except that, unlike JS "classes", it can enforce genuine privacy, in case you care about that. I tried using this cool new pattern by writing an IIFE and exporting a return value, until I realized it was redundant because that is exactly what happens under the hood in I think both ES6 modules and CommonJS.

And if you need subdivisions within that, there are... Functions. IIFEs. Code blocks. Comments. VSCode Regions... Etc. etc...

It sounds like it's an aesthetic preference on your part, and, hey man, fair enough, I think we as programmers need to admit that aesthetics are important to us, instead of constantly trying to justify why our personal aesthetic preferences are "objectively" correct and superior. I'll admit, I have a strong aesthetic preference for an FP style. I think "new" and "this" are just ugly. Just looks like a bunch of clutter. Const and fat arrows and point-free style etc... Just seems more elegant and soothing to me somehow.

But anyway, if you're using classes for "organization", but having only one class per module (file)... I can't image what the point of that could possibly be. If it's aesthetics, you could achieve the exact same appearance with modules and/or object literals.

Object literals are the best part of JS! Going back even to Python, I was like: wait...so to use a function as the value for this key, I have to convert the entire dictionary into "class", which I then have to "instantiate"... WTF are you talking about? I don't need that, I just want this one object right here and now.

I recently refactored a service class in an Angular app that was actually just a bag of functions...even the Angular docs make it clear that that's pretty pointless; the point of a service is to have persistent mutable state that you can share across components.

Of course, you can accomplish that without "classes" too, but... just to group some functions? Come on, man. That's just a ton of boilerplate and extra steps for no actual reason whatsoever. So I refactored it into an FP-style util file that accomplished exactly the same thing 1/3 as many lines.

Sorry for belaboring the point, but I'm genuinely skeptical that there is any good reason for classes to exist as a concept/programming construct. I have not yet personally encountered a situation in which they seem useful/necessary. But I try to be open-minded. Just because I have not yet encountered such a scenario in my own limited experience does not mean such a scenario could not possibly exist.

But if the argument is that they're useful for organizing code? Good lord. I just can't even.

Collapse
 
anewman15 profile image
Abdullah Numan

Also the use of design patterns. Functional programming made space for new design patterns, i.e. in React. Class based OOP has established design patterns that are widely used. We just use them. But, we got to use them when we need them.

Collapse
 
lukeshiru profile image
Luke Shiru Author

That logic could look something like this in FP:

const findUser = find("user");

findUser({ id: userId })
    .then(update({ name: "new name" }))
    .then(save);

// or

create("user")({ name: "new name" }).then(save);

// or

findUser({ id: userId })
    .then(append("projects")({ name: "new project" }))
    .then(save);
Enter fullscreen mode Exit fullscreen mode

Or if you prefer the more verbose async/await sugar:

const user = findUser({ id: userId });
const updatedUser = await update({ name: "new name" })(user);
await save(updatedUser);

// or

const newUser = await create("user")({ name: "new name" });
await save(newUser);

// or

const user = await findUser({ id: userId });
const updatedUser = await append("projects")({ name: "new project" })(user);
await save(updatedUser);
Enter fullscreen mode Exit fullscreen mode

No need for new, no mutations, and lots of reuse and composition.

I understand your point: If you're using a framework that has a class based API, it makes sense. But the main point of this article is that you don't need them for your code.

Collapse
 
squidbe profile image
squidbe

After reading your article, I came to the comment section to make a comment about FP, then saw that you referenced it here, so I'll just mention here that it's odd to me that your article makes no reference to functional programming. I don't think the #functional tag is enough. It'd add clarity if you at least made one overt reference to the fact that you're advocating for an FP approach (which I generally agree with).

 
lukeshiru profile image
Luke Shiru Author

I want to approach this line of articles as just an "optimization" to make code simpler, not a paradigm switch. Still, this optimizations are based on the FP paradigm, so next 2 articles will be about getting rid of if/for/while, and getting rid of variables (immutability).

 
squidbe profile image
squidbe

I understand your desire, but this is definitely more than just an optimization. Just trying to help you communicate with your audience better.

The title could use a tweak, too, because I initially thought the article was gonna be about avoiding taking online classes for JavaScript . 😀

Collapse
 
nssimeonov profile image
Info Comment hidden by post author - thread only visible in this permalink
Templar++

The "more verbose" async/await sugar quickly pays off reducing code complexity when you have multiple statements that should be executed after previous one finishes, which you should nest in the .then(..) statement.

Additionally instead of grouping them all in a User class you will have the same functions with "user" prefix, and having a reasonably well working code completion (intellisense, autosuggest, call it whatever you like) boosts productivity by A LOT. So typing "user." and browsing the suggested methods is another thing you will lose. It's ok if you work on a small scale project alone, because you know what you wrote in the past 2 weeks, but try to understand, that some of us work in a team of 200+ developers on projects, on projects, whose lifespan is well over 10 years and any cowboy approach like this is just... WRONG!

Collapse
 
milabron profile image
milabron • Edited on

I use classes, and I like it better "currently ",
I have never had a problem with mutations.
That superior abtrastion is what I like the most since I'm embracing a shape class with everything it has to do inside, with functional programming it's a bit more messy.

What is the problem with the new and this operator?, does it slow down the software?

 
milabron profile image
milabron

Any examples with the problem of mutation please?

 
milabron profile image
milabron • Edited on

I can also tell you that all the projects I have seen with functional programming are chaotic, instead object-oriented projects are better ordered and more extensible thanks to the interfaces and the inheritance. Why? I don't know, I hope you can answer me. Although I could tell you with object-oriented programming it almost forces you to have an order, while with FP it doesn't.

 
lukeshiru profile image
Luke Shiru Author

I'll go one by one, so bare with me:

I have never had a problem with mutations.

You're super lucky, then. One of the main sources of errors in JS is mutations, which make state unpredictable. If you never change anything, you just create new stuff, then you never end up in scenarios in which the state has a value you don't expect.

That superior abstraction is what I like the most [...]

What's more "abstract" than functions? Classes are already "classifying" stuff, while functions are just transformations of data. Functions can be used directly, or used inside other functions, or used as methods of objects, and so on. The only way of achieving something similar with classes is by using static methods or extending a class with other class (which can be only one), so way less flexible/abstract.

What is the problem with the new and this operator?

There is nothing wrong directly with them (if we don't take into account that "what's this?" is actually a super common problem). But not having to use them, or any of the complexities they bring is just great to make code more simple.

Any examples with the problem of mutation please?

Again, this is kinda common, but basically is accessing something that maybe changed. Without mutations maintenance, testing, reuse, composition and so on is far easier. Instead of modifying state, you pass it around and return something new.

I can also tell you that all the projects I have seen with functional programming are chaotic, instead object-oriented projects are better ordered and more extensible thanks to the interfaces and the inheritance. Why?

Basically you have the answer in your own question. It isn't related to the actual paradigm being used, but to the fact that one project was organized and the other wasn't. A class based codebase can go bad quite easily without organization, there's is a reason behind the classic line about classic inheritance:

"You wanted a banana but what you got was a gorilla holding the banana and the entire jungle"

That line basically means that every time you extend something, it comes with lots of methods that you might no need, which introduces extra complexity you wouldn't had with just a function. This happens more often than not in OOP.

Some comments have been hidden by the post's author - find out more