this post was submitted on 10 Oct 2024
48 points (85.3% liked)

Programming

17416 readers
38 users here now

Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!

Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.

Hope you enjoy the instance!

Rules

Rules

  • Follow the programming.dev instance rules
  • Keep content related to programming in some way
  • If you're posting long videos try to add in some form of tldr for those who don't want to watch videos

Wormhole

Follow the wormhole through a path of communities [email protected]



founded 1 year ago
MODERATORS
top 50 comments
sorted by: hot top controversial new old
[–] [email protected] 12 points 1 month ago* (last edited 1 month ago) (1 children)

In my experience, as a 25-year developer in mostly OOP languages and frameworks, is that people who attack OOP usually don’t really understand it and its usefulness.

And to be fair as it relates to attacking languages or language concepts, I attacked JavaScript without fully understanding it, many years ago. I now understand it more than I ever have in the past and it has some good qualities.

So these days it’s no longer the languages or language concepts I take issue with (though I’ll joke about JavaScript from time to time). It’s the developers who misuse or misunderstand the languages or concepts that irk me. And especially the developers who think being lazy is a virtue.

[–] [email protected] 9 points 1 month ago (1 children)

My issue isn't any particular language but the advocates of various languages treating their language as the best hammer for every nail.

[–] [email protected] 6 points 1 month ago

Damn good point! I feel the same way about CEOs as of late and how they think AI is going to solve everything, even problems they invent just to say they’re using AI.

[–] [email protected] 28 points 1 month ago (3 children)

In this post I use the word “OOP” to mean programming in statically-typed language

So Smalltalk is not object-oriented. Someone tell Alan Kay.

[–] [email protected] 2 points 1 month ago

You down with OOP? 🤪

[–] [email protected] 12 points 1 month ago (1 children)

OOP definitely doesn't get to claim static types for only itself either. Fuck that.

[–] [email protected] 4 points 1 month ago

They don't only say static types. They add classes, inheritance, subtyping, and virtual calls. Mind you, the difference between the last 3 is quite subtle.

So, since I've started nit-picking, Self is also OO and has prototype-based inheritance (as does javascript, but I'm not sure I'd want to defend the claim that javascript is an OO language).

[–] [email protected] 9 points 1 month ago

Yeah let’s not forget the Common Lisp Object System (CLOS) which was more full-featured of an object-oriented language than most “current” languages.

The dynamism allowed both Smalltalk and CLOS to avoid a dark corner that will confound your typical OOP’er today - the circle/ellipse modeling problem; they allow an object to “become” a different type on its own accord. Take that, Java!

[–] [email protected] 32 points 1 month ago (3 children)

OOP is great, and can be much simpler than what you've seen.

It's Java culture that's terrible. The word "Factory" is a code smell all on its own.

Just like any tool, don't overuse it. Don't force extra layers of abstraction.

[–] [email protected] 2 points 1 month ago (1 children)

Why is the word factory a code smell?

[–] [email protected] -3 points 1 month ago* (last edited 1 month ago) (2 children)

It means you’re compensating for the lack of optional/named parameters in your language.

[–] [email protected] 3 points 1 month ago

Sounds like you’re thinking more about the builder pattern.

[–] [email protected] 2 points 1 month ago

Eh? How's that work. I'm not going to sit here and say there isn't too many factories in Java but as a concept it's extremely useful. You hand off a "factory" to something which actually creates the object. This is really useful in for example serialization. How so? You could register factories (mapped to some sort of ID) which get passed the serialized data and return some sort of created object. Now the core serialization code doesn't know nor care how exactly any particular type gets serialized. Pretty nifty huh?

Some languages have better ways to encapsulate this functionality but that's what the factory concept is

[–] [email protected] 8 points 1 month ago (3 children)

I've realized that Factories are actually kind of fine, in particular when contexualized as being the equivalent of partials from the world of functionals.

[–] [email protected] 5 points 1 month ago* (last edited 1 month ago)

IMO factory functions are totally fine -- I hesitate to even give them a special name b/c functions that can return an object are not special.

However I think good use cases for Factory classes (and long-lived stateful instances of) are scarce, often being better served using other constructs.

[–] [email protected] 1 points 1 month ago* (last edited 1 month ago)

I always call my little helper higher order functions (intended to be partially applied) factories :)

[–] [email protected] 8 points 1 month ago (1 children)

I have never seen them used well. I expect there IS some use case out there where it makes sense but I haven't seen it yet. So many times I've seen factories that can only return one type. So why did you use a factory? And a factory that returns more than one type is 50/50 to be scary.

Yeah, I went through the whole shape examples thing in school. The OOP I was taught in school was bullshit.

Make it simpler. Organizing things into classes is absolutely fine. Seven layers of abstraction is typically not fine.

[–] [email protected] 9 points 1 month ago (1 children)

Consider the following: You have a class A that has a few dependencies it needs. The dependencies B and C never change, but D will generally be different for each time the class needs to be used. You also happen to be using dependency injection in this case. You could either:

  • Inject the dependencies B and C for any call site where you need an instance of A and have a given D, or
  • Create an AFactory, which depends on B and C, having a method create with a parameter D returning A, and then inject that for all call sites where you have a given D.

This is a stripped example, but one I personally have both seen and productively used frequently at work.

In this case the AFactory could practically be renamed PartialA and be functionally the same thing.

You could also imagine a factory that returns different implementations of a given interface based on either static (B and C in the previous example) or dynamic dependencies (D in the previous example).

[–] [email protected] 1 points 1 month ago* (last edited 1 month ago) (1 children)

Sounds easy to simplify:

Use one of: constructor A(d), function a(d), or method d.a() to construct A's.

B and C never change, so I invoke YAGNI and hardcode them in this one and only place, abstracting them away entirely.

No factories, no dependency injection frameworks.

[–] [email protected] 2 points 1 month ago (1 children)

Now B and C cannot be replaced for the purposes of testing the component in isolation, though. The hardcoded dependency just increased the testing complexity by a factor of B * C.

[–] [email protected] -1 points 1 month ago* (last edited 1 month ago)

That's changing the goal posts to "not static"

[–] [email protected] 27 points 1 month ago

It's enterprise design that sucks, often it's written in Java - Hello World

[–] [email protected] 22 points 1 month ago (3 children)

Thanks. I hate it.

I'm going to spend this thread agreeing with Rust fans, and I hate that the most. (I love you all, really, but you're fun to try to wind up.)

OOP sucks because Inheritance sucks. This person's brief non-shitty experience doesn't change that.

Languages built around OOP suck where they use inheritance instead of interfaces.

Inheritance is isn't always a terrible choice. But it is a terrible choice often enough that we need to warn the next generation.

[–] [email protected] 13 points 1 month ago (1 children)

Inheritance is isn’t always a terrible choice. But it is a terrible choice often enough that we need to warn the next generation.

But also, when it is not a terrible choice for a problem it is often not the best choice or at the very least equally good as other options that work in vastly more cases.

[–] [email protected] 6 points 1 month ago

ultra rare I've successfully inherited a concrete class, rarely an abstract one and 99% just impl an interface.

[–] [email protected] 17 points 1 month ago (3 children)

If we’re looking at it from a Rust angle anyway, I think there’s a second reason that OOP often becomes messy, but less so in Rust: Unlimited interior mutability. Rust’s borrow checker may be annoying at times, but it forces you to think about ownership and prevents you from stuffing statefulness where it shouldn’t be.

[–] [email protected] 1 points 1 month ago (2 children)

Is there any reason an OO language couldn't have a borrow checker? Sure, it would be wildly more complex to implement but I don't see how it's impossible.

[–] [email protected] 2 points 1 month ago (1 children)

OO languages typically use garbage collector. The main purpose of the borrow checker is to resolve the ambiguity of who is responsible for deallocating the data.

In GC languages, there’s usually no such ambiguity. The GC takes care of it.

[–] [email protected] 3 points 1 month ago
[–] [email protected] 1 points 1 month ago

I would argue that rust has a very strong OO feature set (it just lacks inheritance which is the worst OO feature IMO). It is not seen as an OOP language as it also has a very strong functional and procedural feature set as well and does not favor one over the other letting you code in any style that best fits the problem you have.

So I would not say OO and a borrow checker are impossible or even hard. What makes less sense is a GC and the borrow checker. Though there are some use cases for having a GC for a subset of a program.

[–] [email protected] 4 points 1 month ago

Rust’s borrow checker may be annoying at times, but it forces you to think about ownership and prevents you from stuffing statefulness where it shouldn’t be.

That does sound pretty cool.

[–] [email protected] 11 points 1 month ago (2 children)

To be fair, that's an issue in almost every imperative language and even some functional languages. Rust, C, and C++ are the only imperative languages I know of that make a serious effort to restrict mutability.

[–] [email protected] 3 points 1 month ago

I also love this. I don't why but gc languages feel so incomplete to me. I like to know where the data is that I have. In c/c++ I know if I am passing data or pointer, in rust I know if it's a reference or the data itself. Idk, allows me to think better

[–] [email protected] 5 points 1 month ago (1 children)

How do C and C++ try to restrict mutability?

[–] [email protected] 11 points 1 month ago
[–] [email protected] 13 points 1 month ago

That's a take I can agree with. My experience is that composition solves much of what inheritance is intended to solve and ends up being a more maintainable solution a majority of the time.

[–] [email protected] 27 points 1 month ago (3 children)

Mainstream statically-typed OOP allows straightforward backwards compatible evolution of types, while keeping them easy to compose. I consider this to be one of the killer features of mainstream statically-typed OOP, and I believe it is an essential feature for programming with many people, over long periods of time.

I 100% agree with this. The strength of OOP comes with maintaining large programs over a long time. Usually with ever changing requirements.

This is something that’s difficult to demonstrate with small toy examples, which gives OOP languages an unfair disadvantage. Yeah, it might be slower. Yeah, there might be more boilerplate to write. But how does the alternative solutions compare with regards to maintainability?

The main problem with OOP is that maintainability doesn’t necessarily come naturally. It requires lots of experience and discipline to get it right. It’s easy to paint yourself in the corner if you don’t know what you’re doing.

[–] [email protected] 4 points 1 month ago

This is something that’s difficult to demonstrate with small toy examples, which gives OOP languages an unfair disadvantage.

This is well said. It's such a frustrating meme when I see people talk about how many lines a "hello world" application needs as if that's the benchmark of what makes a language good.

[–] [email protected] 3 points 1 month ago

This is something often repeated by OOP people but that doesn't actually hold up in practice. Maintainability comes from true separation of concerns, which OOP is really bad at because it encourages implicit, invisible, stateful manipulation across disparate parts of a codebase.

I work on a Haskell codebase in production of half a million lines of Haskell supported by 11 developers including myself, and the codebase is rapidly expanding with new features. This would be incredibly difficult in an OOP language. It's very challenging to read unfamiliar code in an OOP language and quickly understand what it's doing; there's so much implicit behavior that you have to track down before any of it makes sense. It is far, far easier to reason about a program when the bulk of it is comprised of pure functions taking in some input and producing some output. There's a reason that pure functions are the textbook example of testable code, and that reason is because they are much easier to understand. Code that's easier to understand is code that's easier to maintain.

[–] [email protected] 7 points 1 month ago

But how does the alternative solutions compare with regards to maintainability?

Which alternative solutions are you thinking of, and have you tried them?

Rust has been mentioned several times in the thread already, but Go also prohibits "standard" OOP in the sense that structs don't have inheritance. So have you used either Rust or Go on a large project?

load more comments
view more: next ›