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.

Tuesday, March 20, 2007

It was easier than I thought

I wanted a way to distribute JUnit tests among multiple threads. I couldn't believe how easy it turned out to be.

 public class ParallelTestSuite extends TestSuite {
private final int numWorkers;

public ParallelTestSuite(int numWorkers) {
this.numWorkers = numWorkers;
}

@Override public void run(TestResult result) {
final ExecutorService executor
= Executors.newFixedThreadPool(numWorkers);
TestResult newResult = new ForwardingTestResult(result) {
@Override protected void run(final TestCase test) {
executor.execute(new Runnable() {
public void run() {
superRun(test);
}
});
}
private void superRun(TestCase test) {
super.run(test);
}
};
super.run(newResult);
executor.shutdown();
try {
executor.awaitTermination(3600, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
}
}


ForwardingTestResult is just what it sounds like; a TestResult that forwards all its method calls to another TestResult.

I found it interesting that I couldn't really figure out how to avoid needing that one-line "superRun()" method. What else could I do?

Anyone out there who understands JUnit and java.util.concurrent have any helpful criticism for how to make this better?

Monday, March 12, 2007

by the way, what does 'extraordinarily typesafe' mean?

I was feeling pretty proud about Guice. It went 1.0 last week and has been making the rounds in the blogosphere the last few days; it seems to make a positive first impression on most people.

I showed my wife an announcement that I sent to some mailing list or other about the release. This was like a few days ago. And just now she says to me, "by the way, what does 'extraordinarily typesafe' mean?"

I said,

"In the software world we really like to think in terms of objects, and our favorite programming languages are built to support this object-centric view. An object might be the name 'Caryn', or it might be Kevin's Discover card or the font that I like to post my blog in.

"When things are not 'typesafe', someone can walk up to you and ask for your credit card and you can hand him a font, and he'll say, '30 seconds.' And he'll walk away, many kilograms away and only later will he gleefully try to charge your credit card and explode cosmically. You know, like what was supposed to happen in Ghostbusters when they crossed the streams.

"Java has always been typesafe at some level, because you can't just pass a font to a credit-card processor. But there was a huge, yawning chasm in the type-safety landscape that an entire Stay-Puft man could fit in comfortably: collections of things. See, you might have a roster full of names, or a wallet full of credit cards, or a drop-down box full of font choices, and as soon as you put something into any of these, it would immediately forget what the hell kind of a thing you just put in. And when you'd take something out, you'd say, 'take this thingamabob out, and by the way, that's a credit card I'm taking out.' Yeah. Only it might not actually be.

"What this is, is it's not typesafe. So anyway, what this guy Josh I work with, who I'm always telling you about, helped to mastermind was this huge change in Java a few years ago, that finally plugged this leak, so that you can now say 'this is a bag of credit cards' or 'this is a bag of employees' and you're only allowed to do things with these bags that make sense. Now Java is finally very typesafe.

"So then there's this thing called Dependency Injection that our framework supports, and other things like Spring also support it, except Spring doesn't support all the latest advancements in Java, and it wants you to configure things outside your Java code, in a bloody text file. So you can go to that text file and say 'use this font renderer to process credit card payments' and it will think that's okay, until you try to run everything and then the asploding.

"But Bob and I made this framework where you get to only write Java code, and so as soon as you try to do this bogus thing, it's just like that example of the bag of employees above. The words turn red on your screen and you know something's wrong right away.

"And that's why we say we're 'extraordinarily typesafe'."

Do you buy it?

Friday, March 9, 2007

This is a blog.

I might start blogging again. I used to blog a ton back in 2001-2004. But, I had the benefit of a job that thoroughly bored me -- and there's nothing like that to inspire a blogger's best and most prolific work. Google came along and fixed all that real good.