Your essay is arguing against a strawman of what "good" OOP is supposed to be. Your examples...
>Objects just aren't supposed to be reaching into each other with 'getters' and 'setters' and messing with information.
The getX() and setX() is an anti-pattern of good OOP.
>Instead of using objects for compartmentalizing functionality, they were just used as a holding pen for loosely related functions.
No, good OOP has class/object with both private state and associated functions combined to provide a public interface for clients to use.
(Although Java & C# (unlike C++) may have muddled this aspect because their language specification does not allow "free" functions to live in a plain namespace. Therefore, programmers are forced to use the "class{}" keyword with static functions as a workaround. Maybe that's where your "holding pen" characterization came from?)
>A traditional OOP approach uses shadowy architecture to remove the responsibility of an object,
No, good OOP tries to push responsibility into classes/objects so they can act as independent agents with minimal coupling to the outer context they sit in.
I'm guessing your comments are based on seeing a lot of poor practices masquerading as OOP which affects what you think OOP actually is. Unfortunately, it's like trying to argue against Javascript because of overuse of "eval()" or arguing the flaws of Haskell because variable names are often 1 character long.
There are definitely problems with OOP but they are not the bullet points you mentioned.
>I'm guessing your comments are based on seeing a lot of poor practices masquerading as OOP which affects what you think OOP actually is
I personally feel that the current OOP model eventually devolves into the bullet points you listed given enough complexity.
One reason why I think this occurs is that there is no concept of a 'message' in OOP, only methods or functions of a class. There's explicit coupling even when calling a method with no arguments because you know the method is valid for that object.
Contrast this with the example of the 'listener' in the room. A speaker broadcasts his message but the independent agents ultimately decide what to do with that message.
The OOP approach calls "object->listen" on each listener. My approach simply broadcasts the message and lets the objects determine how to handle it themselves.
It seems to me like you and the author agree about what good OOP should be (encapsulated state, message passing), except they argue that "traditional" OOP is not useful, and you see Traditional OOP as Good OOP. In fact, "good" is not used a single time in the article so I don't see how they could be constructing a strawman against it. I'm guessing the author is using "traditional" in the sense of what is traditionally taught, which in my experience was closer to Java beans and FactoryFactory classes. In other words, a terrible misinterpretation of Alan Kay's ideas.
If you actually look at the framework they are presenting, it pushes the developer towards many of the "good OOP" points you made. So I'm not sure exactly what you're arguing here.
In a sense the OP is making a point about which paths are paved in most OOP languages. That said, one of the problems I often see in user interfaces is "runaway metaphors". The "objects" and "messages" in question are _metaphors_ and it's important not to let a concept be taken hostage by the metaphors used to describe it.
The main reason controllers exist isn't (or shouldn't be) because of a failure of OOP as a concept, but because of efficiency or complexity. Going back to the baseball example, while it might make intuitive sense for the player to send a "running to first" message, simulating a team's reaction to that event by having player objects communicate purely by messages is horribly complex, whereas a fairly decent simulation can be quickly created by "cheating" with a team controller object (or game controller or whatever).
Even in a "pure" implementation, objects will want to examine each other ("how fast can that player run? Where is she right now? Now? Now? Where's the ball? Who has it? How good an arm do they have?")
If anything, the baseball metaphor shows how complex things can get, and quickly reveals cases where almost any programmer would choose to violate encapsulation to make things work.
I think your comment highlights my biggest problem with OOP: no one agrees on what it is or how to organize it. The amount of mental energy I used to spend on exactly how things should be separated into objects and how those objects interacted was astronomical.
You indirectly put your finger on the real problem with OOP, and that's that a lot of people using it in the wild are frankly not very good at it. Bad object design, not seeing what should be objects in the first place.
I always felt that people who had never used C before C++ or Java, nor any other procedural language, aside from some scripting tended to some of the messiest OO. Seemed they'd want to create a sea of objects and patterns even when something simple was staring them in the face.
OO is harder to teach, and clearly harder to understand. That's the top and bottom of most issues with it.
"Good" OOP and "bad" OOP. There's just no difference. And quite frankly, if the only difference between their "getX" and a symmetrical message passing pattern that retrieves a copy of an attribute is the implementation details then... what?
So much of "Good OO" seems to be prescribing people's mental models and not their actual practices. Seems somewhat odd to me, since most of the models are indistinguishable from one another in practice.
> No, good OOP has class/object with both private state and associated functions combined to provide a public interface for clients to use.
I don't believe there's evidence of this. Inteface scoping (e.g. "private/protected/public") is a relatively recent invention to protect against programmers who "don't know what they are doing". Adequate documentation is a superior tactic since that's a cursory best practice.
The OOP referred to in this article is certainly a straw man, but the alternative suggested has problems too.
Teams that work well act almost like they're mind-reading one another. High quality orchestration of disparate parts is in tension with encapsulation.
Actor-based programming is highly concurrent but in my experience it's harder to reason about as it scales up. Emergent behaviour of interactions between hidden states is sometimes the goal, and sometimes an unwanted side-effect. Network protocols are tricky to get right for a reason; splitting everything out into a shared-nothing message-passing architecture isn't a panacea.
I lean more towards explicit but immutable data structures, and referentially transparent functions over those data structures. In this world, parallelism can increase performance without observable concurrency. Concurrency is the root of non-determinism; non-determinism should be avoided unless it's inherent to the problem at hand.
OOP is a middle ground in this world, but it's ill-defined. Depending on style, it can be stateful and imperative, functional or message-oriented. OOP is not an absolute bad; with effort, it can be massaged, or herded, into working patterns. But it's certainly not a universal optimum.
A traditional OOP approach would have much of the functionality taken out of the player objects, using them simply to hold state.
I don't agree with that characterization. The author certainly has a point that, in some languages in particular, like java, there tend to be massive over-decomposition of problems into reams of factories, controllers, controller factories and controller factory manager factories, but that isn't OOP, that's due to cultural and syntactic issues with those languages. (I know, I know, No True Scotsman.)
In the Rails world, which is a non-trivial component of the broader OOP software world, there is a saying: "Fat Model, Skinny Controller" which is much more in the spirit of what the author is advocating, despite remaining OOP.
Again, this isn't to deny the authors general point, but I don't believe it is bound to OOP, so much as it is to a certain style of OOP coding that arose from early (java in particular) over-engineering and excessive decomposition.
Don't take it seriously. Joe actually admitted, that they only called Erlang OOish because of the OO hype, but Erlang is not an OO language in any way, of course.
>> When I started learning C++ I was shocked. Instead of using objects for compartmentalizing functionality, they were just used as a holding pen for loosely related functions. Instead of communicating between themselves, objects were operated on by some bigger parent object. I found it absurd and fought it for a long time.
This has nothing whatsoever to do with C++. It's like blaming a microwave oven because your kid put a ball of tin foil in it.
C++ is a great language when you are developing big compiled programs and need strong metaphors for decoupling and modularity. Most developers today work on distributed systems where the individual cooperating pieces that they write are much smaller. Your 1000 line HTTP handler in python won't benefit much from strong static type checking, but the linux kernel does, and so do a lot of the infrastructure components we all take for granted every day.
Alan Kay (progenitor of Smalltalk and OOP) has said on various occasions that it should have been called message-oriented programming, rather than object-oriented.
"I'm sorry that I long ago coined the term 'objects' for this topic because it gets many people to focus on the lesser idea. The big idea is 'messaging'"
The same thing happened with REST, TDD and Microservices, etc. The term gets hijacked by people that are less than skilled at executing it. And unfortunately there are more unskilled than skilled people in software; so usually the hijacked term wins the contest. It then takes decades of the "original guy" sending out a BBS message / IRC message / vBulletin post / Blog post / Tweet / HN comment / etc to try to correct people's understanding by saying "actually, that's not canon!".
As a Lua aficionado I hate to see stuff like this:
Ball = {}
Ball.__index = Ball
function Ball.new (x, y)
local table = {}
setmetatable(table, Ball)
table.x = x
table.y = y
return table
end
Explicit setmetatable() call and manual __index setting? You can automate this and hide all the metatable magic = less code to write, less potential for bugs.
E.g. in my own Lua object system the above would be:
Ball = Class()
function Ball:Properties(x, y)
return { x = x, y = y }
end
I think that we suffer from a paucity of precise language around OOP. Everything is OOP, everything is what Kay created, etc.
But that's clearly not the case, and so people have these radically divergent systems of programming within the framework of "object orientation".
My read of this article and it is very message-passing OO to me. Its broadcast mechanism is interesting, for sure. But it reads a lot like Erlang's supervision tree without all that troublesome thinking about a network.
An agent framework I built for my AI research has exactly that.
I use an additional message parameter, automatically inserted and maintained by the framework, which is a list of the messages (FIPA style operation + parameters) from the originating agent forwards to the point of debugging. This gets voluminous and is gated by debug levels.
Message passing in the OOP sense does not imply there's no call stack. Whether or not there's a call stack is an implementation detail.
The same holds true if you actually pass async messages on a bus - nothing stops you from attaching call details to the message. In fact, we have one very prominent async messaging system that does exactly that: E-mail (via "Received:" headers). (And yes, I've used e-mail as a message bus for applications before - Qmail worked great for that)
That's a big down side of a framework for asynchronous message passing (object-oriented or not). E.g. "Flow based programming".
Message passing OOP doesn't necessarily imply that type of message passing. That is to say, the "send" call doesn't have to return until the target object has processed the message.
Also referred to in this context as encapsulation. OOP provides a method of information hiding that allows implementation details to be changed later without changing the public API.
> Objects just aren't supposed to be reaching into each other with 'getters' and 'setters' and messing with information.
In pure Actor Model adding two numbers would probably involve 2+ actors, yet Erlang is not doing this for some reason... I guess on a lower level hardcoding messages makes complete practical sense.
Some meta feedback on the article: this font is too big to comfortably read on mobile, fitting about five to seven words on a line (the typically recommended line length is about 14 words).
The less actual code I see in articles like this, and the more abstraction I read about, the less I take the concept seriously.
In fact, all of this seems like bullshit to me. The actual code inside of the repo is, well, object-oriented. How I interpret this is that the author seems to have no idea what they're even talking about, and that they write more about code than they write code itself.
The more I read about functional programming the more I question why are things this hard in traditional oops. If so many people have been doing oops for so many years then it must have something that is easy to pick up or something that is easier to work with. But after doing some functional programming the number of lines are painfully less and the code is easier to understand. I was thinking may be I was giving too much credit to FP but I was not it's simply just the way things are. But why has FN been used only as an academic tool and not as a goto programming paradigm? Can someone shed some light on this?
Broad generalizations here, but from my understanding -
Back in the dark ages, the performance difference between imperative code and functional code was too large for FP to gain much traction outside of academic circles.
By the time the hardware was anywhere near reasonable, OO had become a thing. People missed some of the key points Alan Kay had, latching on to the one thing that was immediately understandable: objects could model the nouns of your problem domain. That popularity meant that the mainstream was focused on OO, rather than FP.
I went through college in the late 2000s, at a well respected university, for computer science. I had classes devoted to OO(A/D/P); I had none devoted to FP. If you were exposed to it, it was via having to learn a Lisp in the AI class, or similar.
Historically, mostly performance. Outside of academia, performance becomes an important factor in most programs. In the past, both slower computers and fewer optimizations made implementing the abstractions of functional programming ridiculously costly. Once it lost out to imperative programming, it only got harder to change the tide. Performance is now adequate but functional languages lack educational material and libraries as well as industry support.
[+] [-] jasode|10 years ago|reply
>Objects just aren't supposed to be reaching into each other with 'getters' and 'setters' and messing with information.
The getX() and setX() is an anti-pattern of good OOP.
>Instead of using objects for compartmentalizing functionality, they were just used as a holding pen for loosely related functions.
No, good OOP has class/object with both private state and associated functions combined to provide a public interface for clients to use.
(Although Java & C# (unlike C++) may have muddled this aspect because their language specification does not allow "free" functions to live in a plain namespace. Therefore, programmers are forced to use the "class{}" keyword with static functions as a workaround. Maybe that's where your "holding pen" characterization came from?)
>A traditional OOP approach uses shadowy architecture to remove the responsibility of an object,
No, good OOP tries to push responsibility into classes/objects so they can act as independent agents with minimal coupling to the outer context they sit in.
I'm guessing your comments are based on seeing a lot of poor practices masquerading as OOP which affects what you think OOP actually is. Unfortunately, it's like trying to argue against Javascript because of overuse of "eval()" or arguing the flaws of Haskell because variable names are often 1 character long.
There are definitely problems with OOP but they are not the bullet points you mentioned.
[+] [-] rlt3|10 years ago|reply
I personally feel that the current OOP model eventually devolves into the bullet points you listed given enough complexity.
One reason why I think this occurs is that there is no concept of a 'message' in OOP, only methods or functions of a class. There's explicit coupling even when calling a method with no arguments because you know the method is valid for that object.
Contrast this with the example of the 'listener' in the room. A speaker broadcasts his message but the independent agents ultimately decide what to do with that message.
The OOP approach calls "object->listen" on each listener. My approach simply broadcasts the message and lets the objects determine how to handle it themselves.
[+] [-] atonparker|10 years ago|reply
If you actually look at the framework they are presenting, it pushes the developer towards many of the "good OOP" points you made. So I'm not sure exactly what you're arguing here.
[+] [-] Tloewald|10 years ago|reply
The main reason controllers exist isn't (or shouldn't be) because of a failure of OOP as a concept, but because of efficiency or complexity. Going back to the baseball example, while it might make intuitive sense for the player to send a "running to first" message, simulating a team's reaction to that event by having player objects communicate purely by messages is horribly complex, whereas a fairly decent simulation can be quickly created by "cheating" with a team controller object (or game controller or whatever).
Even in a "pure" implementation, objects will want to examine each other ("how fast can that player run? Where is she right now? Now? Now? Where's the ball? Who has it? How good an arm do they have?")
If anything, the baseball metaphor shows how complex things can get, and quickly reveals cases where almost any programmer would choose to violate encapsulation to make things work.
[+] [-] smt88|10 years ago|reply
[+] [-] anexprogrammer|10 years ago|reply
I always felt that people who had never used C before C++ or Java, nor any other procedural language, aside from some scripting tended to some of the messiest OO. Seemed they'd want to create a sea of objects and patterns even when something simple was staring them in the face.
OO is harder to teach, and clearly harder to understand. That's the top and bottom of most issues with it.
[+] [-] kazinator|10 years ago|reply
That messages and (at least single dispatch) methods are identical was settled decades ago.
(Has someone worked out a multiple dispatch OOP based on message passing?)
[+] [-] incepted|10 years ago|reply
> No, good OOP has class/object with both private state
If a class has state, you have to be able to read that state or it's useless.
It's a good practice overall to tell classes what to do but eventually, you need to get information from them. That's where getters come into play.
[+] [-] patrickmay|10 years ago|reply
This needs to be tattooed in reverse on many developers foreheads so it's the first thing they read when brushing their teeth in the morning.
[+] [-] KirinDave|10 years ago|reply
So much of "Good OO" seems to be prescribing people's mental models and not their actual practices. Seems somewhat odd to me, since most of the models are indistinguishable from one another in practice.
[+] [-] jack9|10 years ago|reply
I don't believe there's evidence of this. Inteface scoping (e.g. "private/protected/public") is a relatively recent invention to protect against programmers who "don't know what they are doing". Adequate documentation is a superior tactic since that's a cursory best practice.
[+] [-] collyw|10 years ago|reply
[+] [-] spriggan3|10 years ago|reply
No it isn't, it provides encapsulation, which the most important aspect of OOP.
I agree with the rest.
[+] [-] Tloewald|10 years ago|reply
[deleted]
[+] [-] barrkel|10 years ago|reply
Teams that work well act almost like they're mind-reading one another. High quality orchestration of disparate parts is in tension with encapsulation.
Actor-based programming is highly concurrent but in my experience it's harder to reason about as it scales up. Emergent behaviour of interactions between hidden states is sometimes the goal, and sometimes an unwanted side-effect. Network protocols are tricky to get right for a reason; splitting everything out into a shared-nothing message-passing architecture isn't a panacea.
I lean more towards explicit but immutable data structures, and referentially transparent functions over those data structures. In this world, parallelism can increase performance without observable concurrency. Concurrency is the root of non-determinism; non-determinism should be avoided unless it's inherent to the problem at hand.
OOP is a middle ground in this world, but it's ill-defined. Depending on style, it can be stateful and imperative, functional or message-oriented. OOP is not an absolute bad; with effort, it can be massaged, or herded, into working patterns. But it's certainly not a universal optimum.
[+] [-] carsongross|10 years ago|reply
I don't agree with that characterization. The author certainly has a point that, in some languages in particular, like java, there tend to be massive over-decomposition of problems into reams of factories, controllers, controller factories and controller factory manager factories, but that isn't OOP, that's due to cultural and syntactic issues with those languages. (I know, I know, No True Scotsman.)
In the Rails world, which is a non-trivial component of the broader OOP software world, there is a saying: "Fat Model, Skinny Controller" which is much more in the spirit of what the author is advocating, despite remaining OOP.
Again, this isn't to deny the authors general point, but I don't believe it is bound to OOP, so much as it is to a certain style of OOP coding that arose from early (java in particular) over-engineering and excessive decomposition.
[+] [-] ninjakeyboard|10 years ago|reply
-- Alan Kay, OOPSLA '97
[+] [-] rdtsc|10 years ago|reply
http://erlang.org/pipermail/erlang-questions/2009-November/0...
Joe likes to be funny, so don't get upset and confrontational about it.
The central idea is this I think:
---
I now believe the following to be central to the notion of OO.
All the other stuff (inheritance, private/public methods, ....) has nothing to do with OO.---
[+] [-] incepted|10 years ago|reply
[1] http://harmful.cat-v.org/software/OO_programming/why_oo_suck...
[+] [-] zzzcpan|10 years ago|reply
[+] [-] markbnj|10 years ago|reply
This has nothing whatsoever to do with C++. It's like blaming a microwave oven because your kid put a ball of tin foil in it.
C++ is a great language when you are developing big compiled programs and need strong metaphors for decoupling and modularity. Most developers today work on distributed systems where the individual cooperating pieces that they write are much smaller. Your 1000 line HTTP handler in python won't benefit much from strong static type checking, but the linux kernel does, and so do a lot of the infrastructure components we all take for granted every day.
[+] [-] wtetzner|10 years ago|reply
[+] [-] paulddraper|10 years ago|reply
Alan Kay (progenitor of Smalltalk and OOP) has said on various occasions that it should have been called message-oriented programming, rather than object-oriented.
"I'm sorry that I long ago coined the term 'objects' for this topic because it gets many people to focus on the lesser idea. The big idea is 'messaging'"
http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-...
[+] [-] nbevans|10 years ago|reply
[+] [-] copx|10 years ago|reply
As a Lua aficionado I hate to see stuff like this:
Explicit setmetatable() call and manual __index setting? You can automate this and hide all the metatable magic = less code to write, less potential for bugs.E.g. in my own Lua object system the above would be:
[+] [-] KirinDave|10 years ago|reply
But that's clearly not the case, and so people have these radically divergent systems of programming within the framework of "object orientation".
My read of this article and it is very message-passing OO to me. Its broadcast mechanism is interesting, for sure. But it reads a lot like Erlang's supervision tree without all that troublesome thinking about a network.
But it's approach is still very "OO".
[+] [-] daxfohl|10 years ago|reply
[+] [-] SlipperySlope|10 years ago|reply
I use an additional message parameter, automatically inserted and maintained by the framework, which is a list of the messages (FIPA style operation + parameters) from the originating agent forwards to the point of debugging. This gets voluminous and is gated by debug levels.
[+] [-] vidarh|10 years ago|reply
The same holds true if you actually pass async messages on a bus - nothing stops you from attaching call details to the message. In fact, we have one very prominent async messaging system that does exactly that: E-mail (via "Received:" headers). (And yes, I've used e-mail as a message bus for applications before - Qmail worked great for that)
[+] [-] mpweiher|10 years ago|reply
"Causeway, an open source distributed debugger written in E, lets you browse the causal graph of events in a distributed computation."
http://www.erights.org/elang/tools/causeway/index.html
[+] [-] kazinator|10 years ago|reply
Message passing OOP doesn't necessarily imply that type of message passing. That is to say, the "send" call doesn't have to return until the target object has processed the message.
[+] [-] amelius|10 years ago|reply
[+] [-] kevinavery|10 years ago|reply
[+] [-] sbov|10 years ago|reply
[+] [-] koder2016|10 years ago|reply
In pure Actor Model adding two numbers would probably involve 2+ actors, yet Erlang is not doing this for some reason... I guess on a lower level hardcoding messages makes complete practical sense.
[+] [-] unknown|10 years ago|reply
[deleted]
[+] [-] lucb1e|10 years ago|reply
[+] [-] qaq|10 years ago|reply
[+] [-] andrewmcwatters|10 years ago|reply
In fact, all of this seems like bullshit to me. The actual code inside of the repo is, well, object-oriented. How I interpret this is that the author seems to have no idea what they're even talking about, and that they write more about code than they write code itself.
Show me what you mean, don't just talk about it.
[+] [-] rlt3|10 years ago|reply
Also, of course it's object-oriented. The article is titled a "healthy" hatred for a reason.
[+] [-] CyberDildonics|10 years ago|reply
[+] [-] andrewvijay|10 years ago|reply
[+] [-] lostcolony|10 years ago|reply
Back in the dark ages, the performance difference between imperative code and functional code was too large for FP to gain much traction outside of academic circles.
By the time the hardware was anywhere near reasonable, OO had become a thing. People missed some of the key points Alan Kay had, latching on to the one thing that was immediately understandable: objects could model the nouns of your problem domain. That popularity meant that the mainstream was focused on OO, rather than FP.
I went through college in the late 2000s, at a well respected university, for computer science. I had classes devoted to OO(A/D/P); I had none devoted to FP. If you were exposed to it, it was via having to learn a Lisp in the AI class, or similar.
[+] [-] nv-vn|10 years ago|reply