April 28, 2005
What would you do?
Dear readers of this site, I need your advice.
I'm writing a framework that might get a lot of public exposure, so I'm very obsessive about getting the design right. Currently I'm stuck with an issue where every solution has been "considered harmful" by someone. That's why I'm turning to you for help.
The situation is rather straightforward: A File can be annotated with many Tags. Tags are identified through URIs. There is an interface TagReader to read Tags from a File:
interface TagReader {
Tag readTag(File file, URI tagURI);
}
When a TagReader is told to read a Tag from a File, it might very well happen that there is no such Tag for the File. A missing Tag is not really an exceptional event, but it still has to be handled in some way.
My question is: What should happen when a Tag is not found?
- Return
null The most obvious idea, but there is a case for never returning null even if you have a gun pointed at your head. An important reason why I'm hesitant to go this way is that every consumer of
readTaghas to check its results fornulllest bad things might happen. As I expect many implementations ofTagReaderbeing used together, this is an issue.- Throw an exception
-
I could change the signature of
readTagto this:Tag readTag(File file, URI tagURI) throws TagNotFoundException;What I like about this is that the event "Tag not found" is directly codified into the API. Every consumer of
readTagis forced to deal with the case that the wantedTagisn't present.A downside is that, as mentioned, missing
Tags will be very commonplace and I've been told to reserve exceptions for exceptional things. Also throwing an exception is a very expensive operation, and I can very well imaginereadTagbeing called on 3000Files in a row with most of them bound to throw aTagNotFoundException. Finally checked exceptions make for a lot of code clutter. - Return a Null Object
-
There is an interesting design pattern called the Null Object which could be (rather violently) abused for my purpose. The idea in this case would be to return a singleton object
NullTagthat implementsTagbut plays dead on all messages it receives.Advantages: No
NullPointerExceptions. Downsides: Relies on convention as much as returningnull. Unexpected things might happen if a class is not aware of the possible presence ofNullTagand might, for instance, attempt to change its value. Violates the Fail Fast practice in unspeakable ways.
What would you do?
Update: The conclusion is up.
April 25, 2005
Instant autocomplete in Eclipse
Oh boy. For three long years I believed that autocomplete in Eclipse simply was so damned slow. Three years. My goodness.
Note to past self: Go to Window / Preferences / Java / Code Assist / Auto-Activation and decrease Auto activation delay from 500 to zero. Yes, by default Eclipse waits for half a second before it even considers to help you out.
Will instant autocompletion cause bad karma with huge projects or something? There has to be a good reason for this insane default, and I don't care about it at all. I'm too busy writing down object names, hitting the dot and giggling like a child.
April 21, 2005
The almighty Util class
You are in need of a new method. Various rules of thumb (like the GRASP Patterns) strongly suggest that method belongs to Klass, but the source of Klass is not under your control. What do you do?
Some languages allow you to mess with existing namespaces:
In Ruby you could simply reopen
Klassand glue onmethod.In Perl it is possible to revisit packages but (unlike in Ruby) not everything is an object and modules from the CPAN feature varying degrees of OOPfulness.
Javascript lets you extend classes in a way that makes it completely useless in practice. You see, Javascript's class tree is majorly fucked up and consistently inconsistent in all major browsers. If you retrieve a DOM node, there's no cross-browser way to tell which class it will have.
document.getElementsByTagNamereturns some sort of collection, but it's not anArray. Hell, not even all objects descend fromObject.
When you cannot extend classes like in Java or C#, chances are you will end up creating over-generic Util classes as StringUtil or JspUtil. The honorable Charles Miller blames this practice on the language:
Thus, 'Util' is shorthand for "stuff that belongs somewhere else, but I'm not allowed to put it there, damnit.".
Thoughtworker Darren Hobbs prefers to supply utility methods through concrete objects. For most purposes I agree. It is much nicer to have multiple, highly cohesive utility classes that each do one thing well instead of one bulky class of loosely related procedures.
Take Java's URLEncoder. URLEncoder does one thing which is, well, encode URLs. I will happily reuse a lightweight URLEncoder in my code. I'd be less enthusiastic about a gigantic NetworkUtil library, especially if we weren't talking about a built-in framework class.
I wonder if there will ever be a language which truly embraces Dependency Injection down to the level of its core classes. Even if all a method does is to create and return a String, my application should be able to control which kind of String is produced. I'm aware that this stretches the notion of Dependency Injection and might potentially break a million other things but hell would I love to read about more ideas along these lines!
April 19, 2005
The DRM Experience
When I received my new cellphone I eagerly set out and bought a handful of great Java games, among them the magnificent Alpha Wing. The games arrived through a service message from my provider and played without problems. The world was good.
A few weeks later I had to patch my phone's firmware in order to fix some annoying bugs. In the process I lost every game I bought because purchased games are stored in some super-secure place you cannot backup.
I do not buy games for my phone anymore.
On a summer trip through the Aquitaine the Girl and I had a sudden craving for a particular piece of music. Luckily she had purchased the respective album and brought it along for the trip. We already imagined ourselves rocking out to the music with an Atlantic breeze blowing through our hair, but the car's CD player could not handle the copy-protected disc.
I do not buy copy-protected music anymore.
(Epilogue: Before we began our trip to France, the Girl made me a copy of the mentioned album. I happened to bring along the copy. The car would happily play the copy. We got the message.)
April 17, 2005
Sometimes sloppy is enough
Doug Pardee writes about computer science losing its influence on software development:
There seems to be a single key factor in what makes a CS technique survive: it has to simplify the programming process. A learning curve is acceptable, but once the technique is learned, it must make the creation of programs easier. A technique that complicates the writing of the program in return for a promised future value will not survive.
It might be time for us to admit that often "sloppy" is in fact enough. It might hurt the heart of a computer scientist, but people are nailing together planks and getting away with it all the time. Most of the more advanced techniques and processes tackle problems simply not relevant to those projects, so why do we expect these people to care?
Maybe some of the more sophisticated approaches are not inherently more complicated than their equivalent spaghetti hack, but simply ripe for better packaging. See the recent popularity surge of Rails among PHP developers. I bet that a major part of these PHP-to-Rails converts didn't initially give a damn about MVC or object orientation, but were lured over by the promises of Active Record (the object/relational mapping layer in Rails).
Beyond all the controversy that Rails has generated, little else has brought more developers to the needle of OOP and patterns, ultimately making the world a better place.
Because you can never have too many version numbers
It's one thing to name a product Java 2 Platform Standard Edition 5.0 Update 2 also known as Java 1.5 code named Tiger. But it takes major balls to write up two long guides justifying this hopelessly broken versioning scheme. How priceless is this:
The number "5.0" was arrived at by dropping the leading "1." from "1.5.0". Where you might have expected to see 1.5.0, it is now 5.0
And stupid me actually expected 1.5, like, you know, the version that comes after 1.4! But now I get it! It's really 1.5 but 5.0ishly stable and mature and secure and scalable and stuff, but, like, totally 2nd generation!
I had intended to make fun of what Java might be called in 2015 but quickly found myself unable to outdo the present reality in any noteworthy way.
April 15, 2005
On Uniform Access
If you remember your Programming 101 class, you probably also remember an instructor threatening blood and thunder should he ever catch you directly exposing instance variables to the public. The insistence on using getter and setter methods in Java et al. stems from something called Uniform Access Principle:
All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.
While I'm convinced that the Uniform Access Principle is a dogma worth subscribing to, its implementations often lack the love it deserves. For instance, when the designers of Java decided to go Uniform Access, their solution was not to make methods and instance variables look alike. Instead they agreed to make everything look like a method. That is why Java forces you to write foo.setBaz(bar.getBaz()) when the poet in you really thinks foo.baz = bar.baz.
Things look much brighter in the world of Ruby. Ruby lets you "assign" values to methods, so you can skip the whole accessor mess with getBaz() and setBaz(..). If baz is an instance variable, just expose it as such:
attr_accessor :baz
Should you ever change your mind about the nature of baz you can overwrite its accessors like this:
def baz # funky code for getting baz end def baz=(value) # funky code for setting baz end
Something similiar is possible in vanilla Perl, but it's a pain in the ass. See perltie for the ugly details and my Reformed Perl hack for an example for how to hide the mess.
While C# promises Uniform Access Nirvana in the spirit of Eiffel and Ruby, its implementation falls short in some important points. In C# you may define properties which look like this:
public Klass Baz {
get {
// funky code for getting Baz
}
set {
// funky code for setting Baz
}
}
The issues:
- The property above will mutate into
get_Bazandset_Bazduring compilation. That means while most of your C# source is ignorant of how access toBazis implemented, compiled code is not; so better pay attention when accessing a class using reflection. - It is impossible to overload property setters.
set_Baztakes anotherKlassas its value and that's all you get. - An interface which includes a get/set property cannot be implemented with a public instance variable of the same name. You need to type out the whole property construct instead, even though all it does is reading and writing to a variable.
I hope to have shed some light on the state of Uniform Access. For further discussion of the topic, check out the always excellent Portland Pattern Repository Wiki.
Autoboxing Lists into Arrays
I'm having a blast playing with the new Generics in Java 1.5. Being able to parametrize your types truly fucks with your vintage Java habits, which feels a bit like learning a better way to breathe.
Now that Collections have become so inherently nice, I'd really like to prefer List<Foo> over Foo[]. For most purposes, Lists are about a trillion times more fun to use. Unfortunately there are also a trillion pre-1.5 APIs demanding to be fed with arrays, which makes me brood over the best diet for my own classes.
I wonder if there should be an effort to have Java autobox Lists into Arrays and vice versa? There is already a plugin for IDEA that assists you with the manual conversion procedures, but I'd really enjoy seeing this supported natively.
April 10, 2005
Meet the parents
Should a class be able to disavowe parts of its ancestry?
Assume an arrayish class Playlist. To quickly implement the List interface you might inherit from ArrayList:
class Playlist extends ArrayList
But now everyone can see that Playlist is an ArrayList when all you wanted to admit to is implementing List! Weirdos might cast Playlists into AbstractCollections! People might stuff Playlists into methods working on ArrayLists! Blood will be shed! Chaos will ensue!
So how about something like this instead:
class Playlist extends ArrayList concedes List
I understand one could deny Playlist's ancestry by wrapping an ArrayList in two pages of code, or trust the wisdom of my development team to shun concretely typed instances when possible. But that's not the point. Where Playlist's implementation magic comes from should be a secret between javac and myself. Hiding the ancestry of your classes seems no different than hiding the way they tick inside and my language should assist me in expressing this easily. When you're trying develop a future-proofish API that is going to get a lot of public exposure this detail begins to matter.
What do you think?