51
top 50 comments
sorted by: hot top controversial new old
[-] antithetical@lemmy.deedium.nl 41 points 1 month ago* (last edited 1 month ago)

As a life-long developer in OOP languages (C++, Java, C#, among others) I still think OOP is quite good when used with discipline. And it pains me that there is so much misunderstood hate towards it nowdays.

Most often novice programmers try to abuse the inheritence for inpropper avoiding of duplicate code, and write themself into a horrible sphagetti of dependencies. So having a good base or design beforehand helps a lot. But building the code out of logical units with fenced responisbilities is in my opinion a good way to structure code.

Currently I'm doing a (hobby) project in Rust to get some feeling for it. And I have a hard time to wrap my mind around some design choices in the language that would have been very easily solved with a more OOP like structure. Without sacrificing the safety guarantees. But I think they've deliberatly avoided going in that direction. Ofcourse, my understanding of Rust is far from complete so it is probably that I missed some nuance.. But still I wonder. It is a good learning experience though, a new way to look at things.

The article was not very readable on mobile for me but the examples seemed a bit contrived..

[-] nous@programming.dev 23 points 1 month ago

I think a lot of people conflate OOP and inheritance to mean the same thing. And inheritance is what should get the bad rap. It does not solve any problem I have seen any better than other language features (in particular interfaces/traits can solve a lot of the same problems) but inheritance causes far more problems overall.

But building the code out of logical units with fenced responisbilities is in my opinion a good way to structure code.

This is encapsulation, which is one of the better ideas from OOP languages. Though also not unique to them.

And I have a hard time to wrap my mind around some design choices in the language that would have been very easily solved with a more OOP like structure.

What design choices would those be? And how would they better fit into an OOP like structure? Note that rust is not anti OOP - it uses OOP techniques a lot throughout the code base. It just lack inheritance and replaces that with other IMO better features.

[-] 0x0@programming.dev 4 points 1 month ago

This is encapsulation, which is one of the better ideas from OOP languages. Though also not unique to them.

Interfaces, APIs, mincroservices, the unix philosophy...

Curious to hear what in Rust could be more easily solved with OOP! I think one reason for rust not using OOP is because they want to minimize dynamic dispatch and keep it explicit where it happens, because it's a language that gives you very fine grained control of resource usage, kinda similar to how you have to be explicit about copying for most types. Most trait calls are static dispatch unless you have a Box::<dyn SomeTrait>

[-] nous@programming.dev 12 points 1 month ago

IMO rust does use OOP styles all over the place. OOP does not mean dynamic dispatch or inheritance - that is just what older popular languages used. Note that static dispatch and monomorphization (where the compiler generates a method for each type at compile time rather than using a runtime lookup) give you a lot of the benefits of dynamic dispatch at the cost of binary size in favor of runtime checks and performance.

And other aspects of OOP, like encapsulation, data abstractions and polymorphism are easily achievable in rust and often are. Just look at any object from the std library - they are all essentially written in an OOP style. Such as Vec or File - hidden internal state with traits that you can swap them around with other parts that make sense. The only thing it really likes - like Go lang (which claims to be an OOP style language) is inheritance.

And the only reason rust is not seen as or describes itself as an OOP language is because it does not force the OOP style on you. But instead lets you program in more of a functional or procedural style if you want to. You can pick the best methodology to solve the problem you have at hand rather than trying to fir everything into a single style.

But that does not make it bad at OOP style.

I'm using "OOP" more in the sense that is described in the article, but that is a fair perspective on rust and OOP. It is a term with a lot of different interpretations after all.

[-] Serinus@lemmy.world 31 points 1 month ago

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.

[-] MyNameIsRichard@lemmy.ml 27 points 1 month ago

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

[-] GissaMittJobb@lemmy.ml 8 points 1 month ago

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.

[-] Serinus@lemmy.world 8 points 1 month ago

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.

[-] GissaMittJobb@lemmy.ml 9 points 1 month ago

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).

load more comments (3 replies)
[-] Kache@lemm.ee 6 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.

load more comments (1 replies)
[-] JackbyDev@programming.dev 3 points 1 month ago

Why is the word factory a code smell?

load more comments (3 replies)

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.

[-] dvlsg@lemmy.world 12 points 1 month ago

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

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).

[-] aubeynarf@lemmynsfw.com 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!

load more comments (1 replies)
[-] magic_lobster_party@fedia.io 27 points 1 month ago

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.

[-] BatmanAoD@programming.dev 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?

[-] JackbyDev@programming.dev 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.

[-] expr@programming.dev 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.

[-] wesker@lemmy.sdf.org 25 points 1 month ago* (last edited 1 month ago)

I love working in OOP. It's true that it's not always the best solution for every project, but damn if it isn't nice when it is.

[-] Hawk@lemmynsfw.com 12 points 1 month ago

I've always had an easier time jumping into an oop code base, then eg a lisp one.

I hear people when they say they don't want their data mixed in with their logic but The pressure to structure code Is very nice.

[-] MajorHavoc@programming.dev 3 points 1 month ago

The pressure to structure code Is very nice.

That is a great point. It's interesting how our tools influence our designs, and OOP does force some thought to go into having an intentional structure.

[-] MajorHavoc@programming.dev 23 points 1 month ago

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.

[-] arendjr@programming.dev 17 points 1 month ago

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.

[-] lolcatnip@reddthat.com 11 points 1 month ago

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.

[-] nous@programming.dev 5 points 1 month ago

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

[-] lolcatnip@reddthat.com 11 points 1 month ago* (last edited 1 month ago)

const

They don't do it well, but an attempt was made.

[-] IsoSpandy@lemm.ee 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

[-] MajorHavoc@programming.dev 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.

load more comments (4 replies)
[-] barsquid@lemmy.world 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.

[-] nous@programming.dev 13 points 1 month ago

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.

[-] sip@programming.dev 7 points 1 month ago

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

[-] nous@programming.dev 14 points 1 month ago

Inheritance, which allows classes to reuse state and methods of other classes.

This is the absolute worst feature of typical OOP languages. I don't know of any case where it is the best way to solve a problem and very often becomes a nightmare if you don't get the exact hierarchy of types right. It becomes a nightmare once you have something that does not quite fit into the assumptions you original made when you started out. Which happens all the time.

The examples given with the logger can be solved just as well if not better with interfaces/traits with solutions that don't have that problem.

Composition is far better and immensely more flexible than inheritance. Extracting duplicate code into helper classes or static functions is a good option.

Conformance to interfaces or protocols with default implementations is a great alternative as well.

I like OOP more than other styles, it’s just often badly done. Complex inheritance, huge classes that do too much, overuse of factories and similar patterns, can ruin it.

[-] antithetical@lemmy.deedium.nl 10 points 1 month ago

I do not agree. Very often, when using libraries for example, you need some extra custom handling on types and data. So the easy way is to inherit and extend to a custom type while keeping the original functionality intact. The alternative is to place the new functionality in some unrelated place or create non-obvious related methods somewhere else. Which makes everything unnecessary complex.

And I think the trait system (in Rust for example) creates so much duplicate or boilerplate code. And in Rust this is then solved by an even more complex macro system. But my Rust knowledge might just nog be mature enough, feel free to correct me if I'm wrong..

[-] nous@programming.dev 6 points 1 month ago

So the easy way is to inherit and extend to a custom type while keeping the original functionality intact.

You can do this with traits and interfaces in rust/go. You can add any methods you want onto existing types. Which IMO is better. No need to subclass, in just just create a new trait, implement it on the type you want and you have new behavior attached to that type without needing to convert the existing thing you got from something into a new type.

And I think the trait system (in Rust for example) creates so much duplicate or boilerplate code.

It really does not. You can have implementation on traits that don't need to be re-implemented on every type - like the Iterator - it provides 76 methods of which you need to implement only 1 for new types. You can implement others for custom behavior which is great for specialization (aka using a more efficient implementation for types that have more info, like calling skip on an array which knows all its elements vs the default which needs to call next n times).

But it creates a vastly more flexible system. Take a very basic example - read/writing to something. How do you model that with inheritance? Basically you cannot. Not without painting yourself into a corner eventually. For instance, you can read/write to a file, to a network socket, to stdin/stdout but each of these is very different. Stdin for instance cannot be written to and Stdout cannot be read from. You might want to have a buffered reader/writer as well that wraps these types making read operation cheaper.

You cannot put these into a inheritance tree. Everything either needs to inherit from the same generic base that can both read/write and probably also close. But then for some types you need to implement these methods that don't make sense that do what? Nothing when called? or throw an exception? It is a poor way to model this behavior.

Read and Write are orthogonal ideas - they have nothing to do with each other except they might be useful on some of the same types. With interfaces/traits you are free to separate these and implement them on whichever types make sense for them.

I have not yet seen a problem that is solvable with inheritance that cannot be better solved with some other language feature in a better way. It sort of works for some things, but other solutions also work at least equally well. Which leave it in a state where what is the point of it? If it is not solving things better then other solutions we have these days?

[-] atzanteol@sh.itjust.works 6 points 1 month ago

Yeah inheritance isn't always the best solution. But even Java, the much maligned example for this, doesn't do it for the i/o example you give.

[-] henfredemars@infosec.pub 7 points 1 month ago

I’ve seen some surprisingly fragile OOP solutions that require tons of internal knowledge about how the classes work. It seems to be a popular approach to writing code that just isn’t very flexible.

[-] nous@programming.dev 5 points 1 month ago

It requires you model your problem perfectly from the start, then it can work alright. But in reality you cannot know the future and when new requirements come in that don't fit the model you created you are left in a crappy spot of needing to refactor everything or just cram it in however you can.

[-] henfredemars@infosec.pub 5 points 1 month ago* (last edited 1 month ago)

I recently did some refactoring with injector and composition patterns already there and it was a breeze.

OOP isn’t bad but like anything it requires some care.

[-] nous@programming.dev 3 points 1 month ago

Note that I am explicitly calling out inheritance here rather than OOP as a whole. There are many things about OOP that are not that bad or quite ok, like composition for instance. It is generally badly designed inheritance that leads to

require tons of internal knowledge about how the classes work

And it is very hard to create a good inheritance structure that does not devolve over time as new requirements get added. While there are other patterns that OOP languages have started to adopt in more recent years (like composition and interfaces) that solve a lot of the same problems but in a vastly more maintainable way.

[-] MajorHavoc@programming.dev 3 points 1 month ago* (last edited 1 month ago)

is very hard to create a good inheritance structure that does not devolve over time as new requirements get added

That's such an important point. Whatever else folks take from this thread, I hope they catch that.

And I'll pile on to add - more layers is more risk. One layer of inheritance is a lot easier to keep maintaining than inheritance that goes four layers deep.

[-] nous@programming.dev 2 points 1 month ago

And if you only have one layer then why not just use interfaces/traits? Which are a vastly better design than inheritance.

[-] ulkesh@beehaw.org 12 points 1 month ago* (last edited 1 month ago)

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.

[-] ursakhiin@beehaw.org 9 points 1 month ago

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

[-] ulkesh@beehaw.org 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.

load more comments
view more: next ›
this post was submitted on 10 Oct 2024
51 points (85.9% liked)

Programming

17314 readers
37 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 !webdev@programming.dev



founded 1 year ago
MODERATORS