Wednesday 7 January 2009

Object Initialization

Making sure your Smalltalk objects are initialized once and only once can be tricky.

(if you'd rather skip the discussion and go straight to the details, here are Seaside's object initialization conventions)

For starters, some Smalltalks implement #new on Object to call #initialize and some don't. But it gets more complicated than that. For example:
  • What should you call a new parameterized initialization method?
  • How should a subclass with one of those make sure its superclass is initialized?
  • How should class-side instance creation methods work?
  • How do you make sure inherited instance creation methods don't result in partially initialized objects?
  • What if someone else has made subclasses of yours and is already overriding your superclass' initialization method?
These are just a handful of the possible issues.

For much of the code you write, you can probably avoid worrying about this too much. You were probably using and testing the code as you wrote it, so you know it works. You probably don't have that many levels of subclasses and you (or at least someone on your team) probably knows (or can trace) all the users and subclasses of each class. In many cases, it probably isn't even the end of the world if an object is initialized twice and you're bound to notice pretty quickly if an object is never initialized. All this breaks down as your project and team get bigger, of course.

As a framework, though, I believe Seaside needs to hold its developers to the very highest possible standards of code quality. Seaside is used in Smalltalk images of various platforms all over the world and there is no way that any developer will ever be aware of all the subclasses and users that are out there. Some of our users will implement classes where it does matter if they're initialized twice and when it breaks it will be them not us who notices the problem. This means that we need to be unambiguous, clear, concise, consistent, and portable (and yes, documented... sigh), as well as vigilant in thinking about how developers will subclass, extend, and use our code.

For Seaside, Avi and I worked out a loose convention for this from day one and it has been fairly consistently applied. Some recent discussion on the mailing list, however, has prompted me to formalize and document our conventions. Our requirements are something like:
  1. Each object must be initialized once and only once.
  2. During initialization, all initialization methods must be called in a predictable order. If my subclass has already overridden my superclass' initialization method, it should still be called. (This means a method should never call super with a selector other than its own).
  3. All inherited instance creation methods must result in a completely initialized object.
  4. The conventions must be consistently applicable in all cases.
  5. It should be very clear to users of the class what parameters are required for initialization.
  6. It is more important to minimize the complexity and effort for users of the framework than for developers of the framework.
  7. Without sacrificing the above points, we should not have to write or override more code than necessary (we don't want to have to override every instance creation method each time we subclass, for example).
So here are the conventions we use in Seaside. Let me know what you think.

No comments: