Wednesday, June 27, 2007

Value objects WTF!!?!!1!

Hmm. I just might try my hand at this "blogging" thing after all.

So here's something that has me really mystified about Java.

Let's say you want a simple class to represent a simple value object. Just a garden-variety thingamabob that has -- let's just say two fields. And these two fields together give the object its identity.

And you want this class of yours to be simple, basic, idiomatic and well-behaved.

This seems like a pretty common thing to want to do, right? So far so good?

Better flex those fingers!

  public class Foo implements Serializable, Cloneable {
private String text;
private Integer number;

public Foo(String text, Integer number) {
this.text = text;
this.number = number;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
@Override public boolean equals(Object object) {
if (object instanceof Foo) {
Foo that = (Foo) object;
// oops! I cheated and used a Google-internal helper class!
return Objects.equal(this.text, that.text)
&& Objects.equal(this.number, that.number);
}
return false;
}
@Override public int hashCode() {
// oops! I did it again! ha ha!
return Objects.hashCode(text, number);
}
@Override public String toString() {
return String.format("(%s, %s)", text, number);
}
@Override public Foo clone() {
try {
return (Foo) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e); // impossible
}
}
private static final long serialVersionUID = 0xB0B15C00L;
}


Now, in my native language we have an expression for this situation, which loosely translates as, "what the mother****ing ****???"

What the hell happened? What did we do to deserve this? We just typed forty-four lines of code, not counting any documentation, just to create a ****ing struct, for Christ's sake! And we even had helper methods to call on for everything we reasonably could, at that!

And we'll end up typing it again. And again, and again. Except that in each place in our whole system we discover this pattern, it will have been deviated from in some subtle way or another. Forgot to make it cloneable, Forgot to make it serializable, a typo in equals() -- and tell the truth now, have you never once made that most appalling error of forgetting to implement hashCode()? Really?

So, we'd better write unit tests for all these classes too, then.

Object-oriented languages are meant to save us from duplication. But what we have here is a bona fide disaster. Something has failed us. But what can we reasonably do about it?

This I will address.... in the next post!

Coupling

Update: a crack team of Googlers who constitute the most powery of our Guice power users have assembled a few times now to hash out the design of more than a half-dozen killer Guice 2.0 features with me & Bob. I will say no more (I dropped some hints at the end of the "Becoming More Guicy" tech talk), but just know that I feel ecstatic about how the plans are coming along, and Bob seems to as well. We will give Guice the power to enable you to do more and better things, but we refuse to let Guice bloat with features it doesn't need, and we are extremely protective of the Guice Philosophy.

In the meantime! Christian Schenk has a swell post comparing Guice, PicoContainer, and Spring. Of course, my favorite part is this:

Spring seems to be harder to use than Guice: without autowiring you have to specify the dependencies between your beans in a very verbose manner, i.e. you have to think about the things you’d like to do and write them down in a configuration file. Ideally you don’t have to specify anything with Guice, except the Inject annotations and Guice will handle everything for you.

Ahh, you've just gotta love that shit. I think I'll set it to music.

But Christian's conclusion?

First I opted for Guice because I like experimenting with new, bleeding-egde software. But as I said earlier, all these annotations couple your software tightly to Guice - that’s not very desirable. . . . To draw a conclusion I think choosing between Guice, PicoContainer and Spring, for lack of a hard and fast rule, will be to do what works best for your project.

I want to address this idea of coupling, because I'm noticing that this concern crops up again and again, and I think that the issue has more to do with perception than anything else.

Does embedding @Inject in your code tightly couple your code to Guice? It does in one sense: some version of guice.jar is going to have to be present on your classpath whenever you compile your implementation classes. This much is true. Beyond this, though -- it's really not that bleak. Your classes have no runtime dependency on Guice. If they run as part of an application that doesn't wish to use Guice, the Guice jar file needn't even be present on the server at all! Now, this changes if you want to use more Guice features, like provider injection and injector injection, but this is the case with Spring as well.

Furthermore, the idea of "coupling" implies that "the one cannot function without the other." But with Guice's annotations, this simply isn't the case. It's important to understand that these are only annotations. They are decoration; meta-information. They don't, and can't, actually do anything. They just sit there, innocuously, in case tools will wish to read them, and otherwise have no effect whatsoever. They do absolutely nothing to impede you from testing your code, or from using the classes with Spring.

Perhaps the issue -- and I'm not specifically talking about Christian, but about all the many who have had this objection -- perhaps the issue is a more visceral disdain for seeing import com.google anywhere in your code? Maybe you feel the same way about import org.apache, so you eschew the Apache Commons and all that stuff too? Begone, vile dependencies! Or maybe "that's different," after all, Google is a big old corporation, and I think it might have been on Slashdot the other day that we've Turned Evil now.

In any case, Google doesn't intend to stop open-sourcing our awesome Java shit anytime soon, so you may have to confront that particular issue again and again. Oop, I said too much....

Now, I ask you -- is one extra jar file in your javac classpath really all that bad? Or are there some additional problems that I don't yet understand?

Monday, June 11, 2007

So I went to the new Rasputin...

So I went to the new Rasputin that replaced Tower Records at El Camino and San Antonio, because I had to get the new Elliott Smith double album (awesome). And a strange thing happened.

I was using a new credit card, because my old one expired in 05/07. And the counter dude asked me to sign the back of my card. Whatever, I thought, and so I did. Then he swiped the card, punched his buttons, handed me the stub, and I signed it and gave it back to him.

And he compared the signatures.

Well, I thought it was funny...

Friday, June 8, 2007

There's something fundamentally weird about LinkedHashSet, isn't there?

You use it when you want to have a guaranteed order of iteration (insertion order or LRU order). Except, what would happen if you took your LinkedHashSet and, say, stored it into a HashSet? Later you'd be iterating through the elements of this greater set, and "your" LinkedHashSet will be there, but it might have a completely different iteration order! It might even suddenly be an LRU-ordered set while your original one was insertion-ordered. You'll have no idea what to expect.

So you wanted the iteration order to be respected, and you thought LHS would give you that. But it wasn't respected; it was just discarded.

So... a little bit strange. Just a little.