Sunday, November 30, 2008

Objects 101 - Uniformity

Some interesting comments to my last post have prompted my to expand more on what I believe is good OO.

For me getting to know good OO started out with being skeptical about Bad OO and saying "I don't get it". This is why I think I understand Joe. Healthy scepticism is a good thing, especially when something blatantly just doesn't add up. I could go into a rant about why programmers find it difficult to say "I don't know" or "I just don't get this" and blindly admire the “kings new cloths” even when the king is naked, but I'll leave that for another day :)

As an example of good OO let me reproduce the Smalltalk code example from my response to a comment by Paul Homer to my last post:

3 < 4 ifTrue:[ Transcript show: ‘3 is less than 4’].

Ok lets try and express this in a more familiar OO language, Java:

if ( 3 < 4) System.out.println(“3 is less than four”);

What is bad about this? Like Paul Homer pointed out the instructions (if, <, ..) clearly take precedence over the data (3, 4, ..) in true procedural style. Also there is only one object here "System.out", which given that it is a global static object is hardly an object at all. System.out.println() is no different then ( #include <system/io>) printf(). So the whole statement is not OO, it is procedural and would look pretty much the same written in C.

But Java is supposedly an OO language, so surely I can express this as objects. Lets try:

(new Integer(3)).isLessThan(new Integer(4)).isTrue(new Block {
void execute() {
System.out.println("3 is less than four");
}});

Ok. I've created a DSL here in Java to get rid of the primitives and procedures and express everything uniformly as objects. So what is so bad about this? Well it doesn't read half as well as the Smalltalk example. All those parenthesis and periods tend to obfuscate the intent. Secondly I would need to write my own Integer and Boolean classes, overwriting the ones in the Java standard library. Java's Integer doesn't understand the message 'isLessThan" and Java's Boolean object doesn't understand the message "isTrue". Also the use of an anonymous inner class to simulate a block seems rather verbose, but without closures what else can I do?

So writing pure OO code in Java is difficult to say the least. Does this matter? Well if you are trying to learn OO with an hybrid procedural/OO language, then I think it does. I for one (using C++) definitely found it a challenge.

What do you think?

Paul.

15 comments:

Stephan.Schmidt said...

I'm no Smalltalk expert, but

3 < 4 ifTrue:[ Transcript show: ‘3 is less than 4’].

"Transcript show" looks to me like a "show" method call to a global "Transcript" object. How is this different than a "println" call to a global "System.out" (aka static class)?

And the Java problem arises because true is no object and doesn't have a ifTrue method. A premature optimisation by the Java developers om the 90s which now comes back to haunt them (see the bug prone autoboxing feature in new Java versions)

Peace
-stephan

Paul Beckford said...

Hi Stephan,

I saw the 'Transcript' thing too. In Smalltalk classes are objects too. So whilst Transcript is global like all classes it isn't as bad as System.out.println() which truely is a global procedure, fixed for all time like printf().

So in Smalltalk you can do this:

Smalltalk at: #Transcript put: MyTranscript.

replacing the global Transcript object with a new one.

Autoboxing, yes :) I don't know of the bug but autoboxing always sounded like a bad idea to me, I never use it. If mixing primitives and objects is confusing, then having primitives that can act as objects too is totally mind blowing :)

One last point about classes being global objects in Smalltalk. Gilad Bracha has fixed this in Newspeak by nesting class declarations within other classes which he calls modules, an idea borrowed from Beta. So in Newspeak there is no "static state" at all.

Paul.

Stephan.Schmidt said...

"[...] which truely is a global procedure, fixed for all time like printf()."

No. System.out is a regular object

1.) System.out => java.io.PrintStream@360be0
2.) It can be changed with setOut to mySystemOut for example

Peace
-stephan

Stephan.Schmidt said...

That's the reason it's not called System.println()

Peace
-stephan

Paul Beckford said...

Hi Stephan,

OK. I understand. But what if I have two system objects System1 and System2?

Can I set System to point to System2?

Paul.

Paul Beckford said...

Hi Stephen,

The plot thickens. From the System API:

public static final PrintStream out

final means that the public variable 'out' is read only. So how does setOut work? The whole thing smells IMO. Gilad talks about this too. Why expose a member variable 'out' anyway? It breaks representational independence. We are drifting off topic, but it does demonstrate that this stuff wasn't well thought through. Here is a link to Gilads post:

http://gbracha.blogspot.com/2007/01/representation-independent-code.html

Paul.

Paul Beckford said...

Hi Stephan,

Maybe this example isn't off topic. Back to the System API:

public static final PrintStream out

PrintStream is not an Interface it is a Class. So even if we ignore the final problem, I can only assign objects that are instances of PrintStream to System.out. So my println implementation is fixed.

The only way to change println is to subclass PrintStream and override it.(Thankfully PrintStream is not final so this is possible. I've experience situations where I wanted to do something like this for testing, but was thwarted due to a final class).

This is another failure in representation independence that Gilad alludes to in his post. I should be able to set any object as my output print stream that satisfies the required print stream interface. Java doesn't allow me to do this.

As a client I shouldn't care about the class of an Object, I should only care about its Interface.

I didn't want to get into all of this. What do you think about the difference of the Java versus Smalltalk examples as a learning tool for people new to OO?

Paul.

Stephan.Schmidt said...

Oh, I'm not defending System.out, I would never do this but use an Output Interface with DI whereever I need it (most usually one uses a Logger for this, who would use System.out? Most companies have policies to not use System.out)

But my reply was just about the fact that

"So whilst Transcript is global like all classes it isn't as bad as System.out.println() which truely is a global procedure, fixed for all time like printf()."

Peace
-stephan

Stephan.Schmidt said...

"PrintStream is not an Interface it is a Class. So even if we ignore the final problem, I can only assign objects that are instances of PrintStream to System.out. So my println implementation is fixed."

How so, can't you use subclasses

(Not that I want to defend subclassing, inheritance is really a very bad OO idea and should be abolished)

Peace
-stephan

Stephan.Schmidt said...

For future reference, the call to setOut is native, not sure why, perhaps because of performance reasons or OS limitations.

private static native void setOut0(PrintStream out);

Peace
-stephan

Paul Beckford said...

Hi Stephan,

Yes you can subclass if the target class is not final, but that misses the point.

The thrust of my post is teaching people OO. If your language starts out breaking all the rules how are you meant to learn good OO from Bad?

Transcript isn't ideal either. Objects shouldn't be global, but at least Transcript is a true Object, whilst System is not. Classes in Java are not objects.

I'm arguing for uniformity and consistency where everything is an Object and everything behaves the same way. All these edge cases obscure what an object should be and leads to confusion and bad OO practices I find.

I find that Smalltalk programmers have a much firmer grasp of OO concepts then Java people. Which is why I wouldn't recommend Java as an OO learning tool.

Do you disagree?

Paul.

Paul Beckford said...

Stephan,

setOut is native because it is a back door outside the Java OO programming model. It seems that for security reasons Java doesn't want you changing the output stream without security clearance. Hence the final on System.out. I believe that even using the reflection API that final can't be circumvented, again probably for security reasons.

So setOut isn't a Java method at all, it is probably a direct call into the VM and implemented in C++.

I'm speculating a bit here, but I'm pretty sure I'm on the right track. I think this is just another example of an edge case and inconsistency leading to confusion. Java has several.

Paul.

Stephan.Schmidt said...

"I find that Smalltalk programmers have a much firmer grasp of OO concepts then Java people."

Not sure, I know not enough ST people but some very good Java (OO, DDD) developers.

"The thrust of my post is teaching people OO. If your language starts out breaking all the rules how are you meant to learn good OO from Bad?"

And Transcript is good OO? Not in my book.

"[...] whilst System is not. Classes in Java are not objects."

I thought we were talking about System.out not System as the println call was not to Systen.

"Classes in Java are not objects."

Are you certain? What makes you conclude that Class in Java is not an object? (There are no MC in Java, but that's a different discussion).

"public final class Class
extends Object
implements Serializable"

Peace
-stephab

Paul Beckford said...

Hi Stephan,

I agree that I was a bit harsh on System.out.println(). "out" is an object and the method println() can be changed through subclassing. I still don't like it though.

Transcript in Smalltalk is just another object. In Java this is not the case for lots of reasons. As you mention classes in Java do not have a class (meta-class). But in a class based OO language all Objects have a class. So how can a Java class be an object if it has no class?

Like I say inconsistent. Now the usual cry from Java people is that this stuff doesn't matter, but if you play with Smalltalk for a while and see what this level of uniformity brings then you may agree with me that it does matter, and can change you take on OO completely.

BTW. Smalltalk isn't the last word in OO uniformity. IMO Self is better and Newspeak form Gilad Bracha is better still. But if you've never experienced a uniform OO language like most of the 'OO'programmers out there, how can you judge? You will never know what you're missing.


Cheers,

Paul.

Unknown said...

Your example here resonates with a debate that one of my recent posts triggered.

Basically, I said that any dynamic behavior can be emulated in Java (via function objects, reflection, interpreters, take your pick). Therefore, static analyzers will usually be so unaware of the true dynamic complexity, that they will provide nearly no value to their users.

Your rewriting of the if statement is a good example for this kind of "emulation".

BTW, I ended up writing a small logical proof to formalize my claim. I think you may like it.