17 comments

[ 5.4 ms ] story [ 56.0 ms ] thread
When will the university stop teaching OOP?
When current teachers retire or the market (unemployed students) forces them to adapt the curriculum.
Well, that's the problem.

CS classes teach you to "think like a computer", and computers "think" imperatively.

OOP is inherently a declarative paradigm.

CS classes don't prepare you to think this way. So we get fancy pants CS hotshots writing "objects" that are really just functionally decomposed into classes, and we call the instantiation of those classes an "object".

I would go so far as to say OOP is NOT science. It is philosophy.

>If this can happen, and it does, the author of the Derived class must KNOW how the Base class has been implemented. And they must be informed about every change in the Base class since it could break their Derived class in unpredictable ways.

Yes...? If you are manually overriding implementation details you must be aware of the implementation details and the contract the parent class is meant to fulfill as well as invariants it must hold.

>With Black Box programming, we can be completely ignorant of the implementation since we cannot inject code into the Base class by overriding one of its functions. We only have to concern ourselves with the Interface.

>This trend is disturbing…

it is not a trend and it should not disturb anyone. It seems author wants to change the behaviour of a type... without knowing or understanding the behaviour to start with.

>But if a parent and child could arbitrarily switch places, then clearly something is wrong with this model.

If a parent and child can arbitrarily switch places then it's obviously not a hierarchical model. Classes are not folders, they are a convenient subset of type theory. A 'Company'-related file is not a subtype of a 'Document'-related file, or vice versa. They aren't related, let alone related hierarchically.

>If you look at the real world, you’ll see Containment (or Exclusive Ownership) Hierarchies everywhere.

>What you won’t find is Categorical Hierarchies.

You won't find any hierarchies in the 'real world', they are abstract mental models that we, humans, use to organize things. Which one is more applicable depends on what you're looking at and thinking about.

I won't comment in depth on the latter 2 sections as they're comparatively short and nonsensical.

Don't get me wrong, I love FP and write Haskell and Elm relatively frequently, but this is not an indictment of OOP as much as it is an indictment of poor practices. And I know that's thrown around a lot as an excuse for the real pitfalls of OOP, and I will refuse to say that it's not "true OOP" for that reason, but, it's at least not good OOP.

Delegation is the right way to go. Containment hierarchies is a nice way to phrase it. Roman numerals had fervent defenders back in the day. You can use OOP in a sane way if

1. You avoid extends at all costs (avoid inheritance)

2. All classes are declared final, implement one or more interfaces ( you get polymorphism and abstraction)

3. Split data and code and use immutable data or declare all data as private with no public getters and setters (more encapsulation)

4. Ignore anything ever said by OO thinkers and architects because they will eventually ask you to do the above three with a domain specific language in 30,000 words

This is pretty accurate.

I'd also throw in "Don't use static functions" and "Write declarative code".

Honestly, I think getting rid of public getters and setters goes a LONG way toward more reasonable objects.

The title should be updated to mention that this was written in 2016.
The thing is... functional programming has its own version of a lot of these problems, as well as its own problems.

The easiest example is the base class fragility problem. This is just a special case of the compounding fragility of any reusable component, where fragility increases with the width and depth of its dependents. If, instead of a class, a function is reused by many other functions it will suffer the same problem. Any changes to that "base" function will affect all functions which invoke it down the dependency chain.

I believe the actual issue is that isolating and defining abstractions from the landscape of necessary implementations is actually very difficult. AKA "programming is hard".

I think in a lot of cases it is impossible to correctly model abstractions, since different use cases will push and pull on different parts of the "boundaries" of the given abstraction. In other words, reality is messy and our computer models are usually extremely naive.

Perhaps the valuable takeaway to this is what we have is good but not enough? I don’t have any profound insight here, but I think it’s at least encouraging to know that there could still be some innovation in language design out there to be discovered yet, and we’re not just doing it wrong all the time because we’re absolutely daft. Rather, it’s a refreshing admonishment to hear there is no magic bullet and yes, sometimes code will have to be a little sticky.
That's called the Expression Problem, there is no free lunch.
Indeed, functional programming can lead to spaghetti code just as easily as any other paradigm. I’m really growing weary of all these posts around the Internet claiming that some particular style of development is better than another, it’s such a very shallow metric.

In this particular article, I’m with it on inheritance, I haven’t used it in many years. But I don’t at all follow the author’s argument against encapsulation.

>But I don’t at all follow the author’s argument against encapsulation.

The authors argument is very simple. Encapsulation means everything contained inside an object CAN only be manipulated by THAT objects public methods.

He is saying the concept of encapsulation breaks down when you pass by reference. If the "A" object holds an internal reference to an external "B" object and you manipulate that "B" object externally without going through the api methods of the "A" object you just broke encapsulation by changing the behavior of A by manipulating B externally.

It's not even really an argument whether or not this actually occurs. It does occur as shown below. The only argument that can be made in favor of OOP in this case is whether it occurs often enough to be bad or whether breaking encapsulation occasionally is that bad in the first place. But make no mistake... encapsulation breaks in OOP period. IT IS a fallen pillar.

   class B:
      def B():
         this.X = "Hello"

      def mutate(x: str)
         this.X = "I've been changed by {x}!"

   class A:
     def A(x: B)
        this.B = x

     def mutate()
        this.B.mutate("A")

     def displayB()
        print(this.B.X)

    b = B()
    a = A(b) // b is passed by reference. 
    a.mutate()
    b.mutate("B") // <---- encapsulation broken, b which is referenced inside of 'a' but is mutated outside of 'a'. 
    a.displayB() // <----- should display "I've been changed by A!" if b is encapsulated by a. 
    //However because encapsulation is broken will display "I've been changed by B!"
Generally this problem represents a class of errors that don't exist when all values are immutable (AKA functional programming). It is also fixed in a unique way by the borrow checker in rust.

Simply put it doesn't matter how good someone is as a developer. This is a case where the language paradigm simply prevents a certain error from happening. A bad developer has a 100% chance to avoid this error in a functional language while even the best developer always has a chance of this error occurring in a non-functional language. Language categorically influences quality of code and probability of spaghetti code occurring.

Also, since all objects are generally passed by reference and Since object composition is a pattern common to OOP, the following is a generally true statement:

    Encapsulation is frequently broken in OOP. 
Understand? If you don't understand I can explain to you the fundamentals of OOP and FP and how they are different context of programming in general.
I don't necessarily think paradigms are the problem. The real problem is too much code is just that - too much code. Projects grow to huge sizes, and anyone under time pressure to either produce or extract code under business pressures is bound to get frustrated.

In an ideal world, regardless of the paradigm we choose, we should be looking to do more with less code.

For instance (and this is rather extreme example) use a dictionary in Python that occasionally pickles its contents to a disk for your mom and pop bread shop, as opposed to a full Docker / SQLite / React spin up.

I always believed composition and interfaces was saner than inheritance, but this was sneered at during the 90s and 2000s.

Still, OO isn’t all bad.

It’s perfectly suitable within a self-contained project or library. i.e., a hidden implementation detail.

But there are API land mines if you expose non-final classes for public use.

API design is fundamentally hard, and every language reverts to some sort of versioning.

You are exactly correct, the author has a misguided perspective. Inheritance is rarely considered these days to be the hallmark feature about OO. Composition and encapsulation far exceed inheritance in importance in nearly any software development circle.
Every once in a while I see this article, and every once in a while I fear that junior programmers are going to read it as if it is at all accurate.

This is a great article that represents the fundamental problem of OOP - that developers don't actually use it.

Instead, we have a lot of procedural programmers writing code that masquerades itself as "object oriented", when in reality it's imperative functional decomposition that we're calling OOP because we don't know any better.

I'm not sure what you're getting at here but JAVA code is really OO when compared to something like Go.

If you consider java to not be OOP then it's a completely different story. If you consider java to not be OOP what would style would you call it? Is this what you're referring to with "imperative functional decomposition"?