Looking back, I'm amazed at how long it took me to awaken from the dogmatic OO slumber. One of the things that snapped me out of it was realizing how mechanical and low-level the GoF patterns are. Combine that with the tight coupling you get in object models (a foo has a bar and a collection of bazzes, each of which has a bizzat, and they each tell two friends, and so on and so on) and you end up with very rigid code. That medium is concrete and concrete hardens awfully quickly.
Nowadays, my programs are orthogonal sets of functions that I can combine any way I want to, with no object model to wrestle into submission, and my world is blissfully design-pattern-free.
Freud said sometimes a cigar is only a cigar. I say sometimes a function is only a function.
Edit: I exempt Smalltalk from this generalization about OO. I've met too many smart, well-informed people who prefer Smalltalk (at least half a dozen!) to believe that what Smalltalk programmers do is OO of the kind that I experienced. Alan Kay's writings on the subject confirm that.
If you're exempting Smalltalk from your generalizations about OO, then your observations probably aren't even about OO so much as the fallout from C++ and Java's attempted graft of it onto C. That's like exempting Haskell from generalizations about functional programming.
OO systems can be simple and incredibly useful when solving some problems (without needing tremendous amounts of boilerplate code), but many things claiming to use OOP are doing so for buzzword-compliance. You can, of course, write good OO code in C++ and Java (or even C), but many people working in them do not understand OO design very well (overusing inheritance is especially common), and you will have to write complex and verbose workarounds for fundamental differences in language semantics (e.g. the lack of Smalltalk-style blocks, C-style static typing's requirement for explicit casts).
If you want to understand OO, look at Smalltalk (_Smalltalk-80_ is a good book). Post-Smalltalk prototype-oriented languages such as Io (http://www.iolanguage.com/) and Self (http://research.sun.com/self/language.html) also have numerous interesting ideas in them* , but writings about them generally assume familiarity with Smalltalk. Also, read Alan Kay. Whether or not you agree with his conclusions, he writes lucidly about many language design issues.
My favorite OO example from Smalltalk is that the language doesn't need a primitive for "if", you just pass an object that calls one of two blocks, depending on whether it receives true or false. A Smalltalk block, by the way, is an anonymous function which is not evaluated until its result is needed. Sound familiar? :)
Note: I mostly wrote the following before I had my coffee. Consider yourself warned :)
your observations probably aren't even about OO
I programmed professionally in OO for years, have trained many people in OO design and domain modeling, and know what the fuck I am talking about. I know what Smalltalk blocks are, I know what Smalltalk "if" is, and I happen to have read Alan Kay, which you might have seen had you read my post properly instead of just dumping out a pile of predigested tutorial boilerplate.
My critique of OO comes from struggling to build serious production systems with it, not from programming language parlor games.
exempting Smalltalk from your generalizations about OO ... [is] like exempting Haskell from generalizations about functional programming
No, it's not. Kay coined the term OO, but it hasn't meant what he meant by it for a long time. I'm talking about OO in the sense that the overwhelming majority of people now understand it: roughly, the organization of programs into classes that bundle state and behavior in ways that attempt to model the problem domain. Whatever makes Smalltalk great, it isn't this, as the subsequent history of OO makes clear. People thought that this was the contribution of Smalltalk and they tried to abstract the paradigm out and improve on it - but what really happened is they threw the baby out with the bathwater, and now we have static bathwater. I have a hunch this is because they paid too much attention to the Simula strain in Smalltalk and not enough to the Lisp one (http://news.ycombinator.com/item?id=288786). Had they listened to Alan Kay they wouldn't have made this mistake; there's no question which he emphasizes more.
OO systems can be simple and incredibly useful when solving some problems (without needing tremendous amounts of boilerplate code)
Anything is simple and useful when you're working on a simple problem. What matters is how it works on complex problems. The claim made about OO for a generation now is that it helps to build complex systems, and this mantra has been repeated and taught so often that most people just believe it. It's not true. In my experience, you do typically end up needing tremendous amounts of boilerplate code when working that way (whether or not you see it as such is a different matter). Worse, your system starts to fight itself because the way you need to organize it over here doesn't work over there, and so on; time to factor out the code you need into a static "utility" class - poof, there just went your OO paradigm.
Ultimately, I'm exempting Smalltalk from my critique because of the respect I have for the Smalltalk programmers I know, who can't possibly only be doing OO in the majoritarian sense. I haven't used Smalltalk enough myself to have a clear idea of what they're really doing, and Bob Dylan said not to criticize what you don't understand.
I'm not, however, exempting CLOS, which I have used enough to experience several of the limitations I'm talking about, enough so that I don't bother with it anymore. Perhaps that will persuade you that I'm talking about more than C++ and Java?
First, I saw that you said that you have read Alan Kay, but I'm speaking to the general discussion audience. And, ok, you know what you're talking about; I'm used to reading people who think that all OO systems necessarily suffer from the same problems as e.g. C++'s, and, not knowing
your programming history, had few impressions to the contrary based strictly on what you said. Nothing personal. Be civil, have your coffee.
> What matters is how it works on complex problems.
Agreed. I don't believe pure-OO (or pure-FP, pure-declarative, etc.) is an ideal approach for anything. OOP is a technique that works well for some problems, and is best understood so you can use it when it is a good fit.
I think that the best way to learn OO is via studying Smalltalk. I think the best way to learn FP is via either Haskell or ML, etc. I do real work in OCaml, Lisp, and Python (though I am coming to prefer Lua over Python), but use a multi-paradigm approach based on what I've learned in other languages.
That made me laugh! Actually the coffee kicked in as I was writing, which you can probably tell. I don't know why I didn't go back and edit the first part... I compulsively edit everything else.
Wow never thought of it that way. Can you provide an example of what your programs might look like? I'm pretty interested to know what style you are employing.
There's very little to it. As I said, sometimes a function is only a function. I'm wary of providing an example, because the discourse on programming languages is distorted by being conducted at the level of code snippets when what really matters is whole systems, which are harder to talk about. But since you asked, I went through my code and picked at random the first reasonable example of what I'm talking about:
(The ugly return% is because this code also compiles to Javascript, but that is another story.)
See? It's just a function. In the past, I would have made a Range class with an Intersects method. Now, I don't, and my life is better.
What's valuable is that this function isn't tied to other functions and it isn't tied to any state. That's what I mean by orthogonal. If I want to use this somewhere, I just use it. I'm not forced to pick up a whole glob of other stuff that's stuck to it. I'm not forced to create anything in order to call it - except a couple of lists of four numbers, if I don't have them handy, but this kind of thing is an order of magnitude more lightweight than defining specialized types and creating instances of them; especially since, if your object model is a rich one, your types inevitably reference other types, which makes creating things hard and forces you to jump through hoops just to get at one lousy function. Eventually people create entire technical disciplines called things like "dependency injection" and frameworks to jump through the hoops for them. No one seems to notice that they're taking a complicated thing and making it more complicated.
You may object that writing "new Range(c1,c2,r1,r2)" isn't much different than "(list c1 c2 r1 r2)". All I can say is, yes it is, utterly different in a way that has profound implications for how I build programs - but this isn't the sort of thing that a code snippet can make clear.
What's really going on here, I think, is that "functional programming" is just a tautology. No one has programmed without functions since the 1950s (assembly language excepted). But the facilities for defining functions were low-level for a long time, which made complex programs hard to write. One way of looking at OO is that it was an attempt to fix this problem by adding the abstractions of class and method over top of low-level functions. This turned out to be a mistake, I think. A much better fix is to just have higher-level functions.
Nowadays, my programs are orthogonal sets of functions that I can combine any way I want to, with no object model to wrestle into submission, and my world is blissfully design-pattern-free.
Freud said sometimes a cigar is only a cigar. I say sometimes a function is only a function.
Edit: I exempt Smalltalk from this generalization about OO. I've met too many smart, well-informed people who prefer Smalltalk (at least half a dozen!) to believe that what Smalltalk programmers do is OO of the kind that I experienced. Alan Kay's writings on the subject confirm that.