top | item 7377250

Writing OOP using OOP

105 points| kapilkaisare | 12 years ago |raganwald.com | reply

26 comments

order
[+] barrkel|12 years ago|reply
This is a sociological problem.

The further up the abstraction hierarchy you customize things in languages that let you do this, the more differentiated your code is and - generally speaking - the more awkward it is to integrate with other people's code.

Lisp and Smalltalk are at one extreme end of the scale here. They let you define rich DSLs. Unfortunately, application of their most powerful features to any given problem domain typically results in a custom language specific to that domain - so much so that you lose much of the benefits of sharing a language with other programmers. Understanding what's going on takes much more work, because you need to understand more of the system before its gestalt becomes clear.

DSLs are excellent when they're applied to problem domains that are otherwise very clumsy to express, when the problem is widespread, when it doesn't intermix too deeply with other domains (i.e. the abstraction is mostly self-contained), and when there is a critical mass of programmers using it - enough that understanding the system at its own level of abstraction is enough for most people.

But when you apply the same techniques to relatively humdrum problems like object construction, function binding etc., it's best if the language or its standard library has a reasonable approach and everybody uses it. Developing your own ad-hoc language around class construction means that everyone coming into your code needs to understand it before they can truly understand what the code is doing. It's a barrier to understanding, and the pros of customization need to be substantially higher than the costs of subtlety.

In short, it's usually better to be explicit.

[+] camus2|12 years ago|reply
totally agree.

Take ExtJs core, it's a good exemple of over abstracting everything. But JS is also guilty,guilty of providing "weak" and loose structures.If JS had classes(like ES6),nobody would create all these complicated frameworks to build them.

[+] gruseom|12 years ago|reply
This is really an argument against higher-level languages in general. A complex program written in a lower-level language will certainly be more explicit, and because of that many individual lines will be easier to read. "INC EAX" is as readable as it gets, by that standard. But what you correctly call the gestalt of the system is precisely what won't be easier to see, because you have so much more code to work through. The idea that a system becomes easier to understand when its codebase is much larger is absurd. One major reason for making a system smaller is that its design becomes more accessible, and so in turn does the meaning of its parts.

In my experience, the kind of language hacking that goes on in Lisp systems is not as you describe it. Rather, it's akin to what any programmer does when extracting duplicate code into a function. A better word for this than "DSL" is "factoring"; Lisp's strength is that it offers a simple and powerful way of factoring that is harder in other languages.

I can't comment on Smalltalk, except to say that the way you've lumped it in with Lisp makes your argument weaker. Those two languages achieve their flexibility in such different ways.

[+] klibertp|12 years ago|reply
Knowing a bit of both Smalltalk and Lisp I have to disagree with you.

First, how easily integrated your code is with some other code depends on how much of underlying semantics the two pieces of code share. Both Smalltalk and Lisp are quite minimalist in terms of semantics, which means that everything you write in them will share large part of its semantics with any other code. At some level in Lisps all code is full of conses and in Smalltalk is just objects with messages (I'm over-simplifying ofc, but you get the idea). I know that "at some level" all code is just a list of instructions for CPU - but you get to this common denominator much quicker in Lisp and Smalltalk than in most other programming languages, which makes programs written in both of them more composable, not less.

Second, how easy a DSL is to understand depends on the "L" - after all it's a language, and you most certainly can create completely undecipherable language. That's not a problem with DSLs, however. There are many bad DSLs, but there are also many pretty and readable ones. Some examples I just thought of: http://docs.racket-lang.org/reference/Command-Line_Parsing.h... in Racket and http://pharobooks.gforge.inria.fr/PharoByExampleTwo-Eng/late... in Pharo. They are both readable and easily composable, of course only if you know the semantics of underlying language, but even this is easier because there are so few of them in both languages.

Third, metaprogramming, meta-object protocols, macros and so on are indeed easily abused and you should avoid using them if a simpler abstraction will suffice. But there's some point where you find yourself repeating the same patterns over and over, despite already using all the lower level abstractions. This is where you should use one of higher level ones - contrary to what you say it can make overall readability of the code better. For example, Django ORM does quite a bit of magic behind the scenes - but this means that all this magic is gathered in one place instead of being scattered through all the models in your project. And it makes simple cases (which are probably a majority of cases anyway) much shorter than they would be otherwise - and being shorter (not on its own, see http://weblog.raganwald.com/2007/12/golf-is-good-program-spo...) improves readability.

In short, the problem is as it has always been: bad (domain) languages, unnecessary abstractions and too rich base languages. Next time you have an opportunity to design a good DSL, or fitting high-level abstraction or macros library, just do so. Just try it on your own quite a few times before introducing some of these techniques into production system or team environment.

[+] michaelsbradley|12 years ago|reply
Joose[1] took these patterns about as far as anyone has (open source) as of several years ago; it's based on Moose[2] for Perl.

When Bill Edney and Scott Shattuck finally release TIBET[3] upon the world (something like 15 years in the making), it will take "real OOP" for JavaScript to a whole new level. TIBET is basically a full-on Smalltalk system (including a kernel, images, modules) for JavaScript, but it's not a compile-to-JS kit; rather, it builds right on top of the language, kind of a "super library" for ultra heavy-duty browser based apps.

[1] https://github.com/Joose/Joose

[2] http://search.cpan.org/~ether/Moose-2.1204/lib/Moose.pm

[3] http://technicalpursuit.com/

[+] userbinator|12 years ago|reply
But if we do buy the proposition that OO is a good idea for our domain, shouldn’t we ask ourselves why we aren’t using it for our classes?

"If X is a good thing, why aren't we using more X?" No. I've seen horribly convoluted and overly complex code written as a result of following this sort of dogma with things like design patterns. OOP can be incredibly useful when applied correctly, but don't think that it's a panacea or that "OOP-ness" is somehow directly correlated with code quality. Layers of abstraction and encapsulation are very good at hiding bugs too, and IMHO they're overrated.

[+] Myrmornis|12 years ago|reply
The basic proposition of OO is that objects encapsulate their private state.

I'd say the most basic proposition is that you solve problems by representing the nouns in the problem domain as objects in software. You could have a version of OOP that doesn't involve hiding internal state.

[+] weland|12 years ago|reply
> You could have a version of OOP that doesn't involve hiding internal state.

I think that would make writing sentences with those nouns somewhat difficult.

[+] kybernetikos|12 years ago|reply
> You could have a version of OOP that doesn't involve hiding internal state.

That is in fact the default form of OOP in javascript.

[+] Jare|12 years ago|reply
I generally prefer to KISS than to OOPOOP, but if you are building a large, sophisticated framework for large, long-term projects with large, stable teams, then yeah, it is a good idea to turn your patterns and structural standards into semi-custom languages within the language.
[+] currywurst|12 years ago|reply
Isn't this the scenario that is going to be solved by ES6 'class' syntax sugar?
[+] jkrems|12 years ago|reply
No, that will only allow to subclass "Object", not "Class". Also with ES6 you have to define the prototype all in once and you can't call `defineMethod` for each one, the ES6 sugar is more like just using `MyClass.prototype = Object.create(BaseClass.prototype, { /* properties */ });` today. Which the author apparently doesn't feel is even worth mentioning.
[+] otikik|12 years ago|reply
I like it as a theoretical exercise. I would not use it in production.
[+] irahul|12 years ago|reply
I prefer to keep things simple.

https://gist.github.com/rahulkmr/9482010

It makes working with teams easier.

[+] jrub|12 years ago|reply
In the case of User.getName, why do you re-define that method just to call the prototype's method? Doesn't creating the object from the parent prototype bring that getName method to the User object for free?

Or, were you demonstrating the method overriding + calling to parent use case?

[+] VeejayRampay|12 years ago|reply
Very nice article. Though, syntax highlighting would really add to readability of the snippets. Good job though.