Saturday 1 February 2048

May happen! ..... So what? (over-engineering qualms)

Common wisdom amongst software developpers: if something bad may happen then it will happen! So if your design is left with gapping holes be sure someone will fall into those! But if you happen to know about those "holes" there is another nagging question: is it possible or economically sound to fill in those gaps?

That happened to me a long time ago: I implemented a SQL interpreter and realised that some properties of my code may lead to hog the runtime , or even crash it. As I was worried by that I talked with other developpers and one came with this startling assertion: those conditions are simply not going to be met in "real life"!

We were stuck in a dilemna: we had no proof that it could not happen but trying to fill in the gaps would cost a lot in terms of work and performance. So we decided to hide the gaps and wait: the intuition that nothing was going to happen there has been proved to be practically right during all the product's life.

This example could barely be used for other developpers: it is an anguishing situation to know that something MAY be wrong! This said overanguished developpers may also be fighting windmills: this may happen ... but will it happen? The best thing to do is to prove that it could happen or not... but are we able to provide this proof?

Happily it is sometimes possible to question defensive programming with arguments (provided we do not stick to the rules just to relieve our anguishes).

Though this is a far from perfect illustration I would like jump at the opportunity to emphasize the importance of questionning engineering decisions that could stem from sheer habits.

So again java code:
More or less a typical example about the Singleton pattern :
public class ASingleton {
 // a private constructor
 private ASingleton() { .....}
 // a static instance (could be initialised lazily or in a static block)
 private static ASingleton INSTANCE = ..... ;
 // factory method
 public static ASingleton getInstance() {
  // returns INSTANCE one way or another
 }
}

A rule of thumb is that if ASingleton is Serializable then one should provide :

 private Object readResolve() {
  // returns INSTANCE one way or another
 }

This last code should be discussed: the idea of readResolve is that if your read an instance from an ObjectStream then you may end up with different instances of the Singleton! So a good defensive practice is to make sure there is only one instance of the singleton ....

This may happen .... So what?
This should be discussed and we need to go a bit further than the constraint "one instance":

Point 1 : we may need only one state ... but we do not necessarily need one instance!


How about a singleton written that way:
public class Service {
 // could be from a private inner class
 private static ServiceImplementor INSTANCE = .... ;
 // a constructor!
 public Service() {}
 // every method is delegated to a static service
 public Thing method() { return INSTANCE.method() ; }
 // may be override some methods from class Object
}

In that case:
  • the client code is not necessarily aware that it is using a singleton (this property is hidden to client code)

  • the unicity of the service is nonetheless fulfilled! This, by the way, is coherent with the definition of an object as a device that do something: client code is not supposed to know how it is done.

  • if the client code wants to use the object for its monitor (synchronization, wait/notify) or wants to use the == operator, it is not going to be possible ... hey! wait a minute: the client code is not supposed to know that it is using a Singleton.

Point 2: most singletons are about a context!

  • Most singletons represent something which is external and bound to a context (a System, a spool, a configuration) and the question is: to what context are we bound? a J.V.M? a ClassLoader? a Thread (ThreadLocal objects)? So it is not that uncommon to get different instances representing the context service (even unwillingly: in the case of ASingleton above you may get different instances in a J.V.M if different ClassLoaders are involved).

  • When representing services bound to a context then those singletons are either not serializable or their instance is a representation of a remote context. That is: if JVM1 is sending an instance to JVM2then this instance is representing the context of JVM1 on JVM2 (a remote reference to a service). In that case there could be two different references on JVM2: one of the local context and one of a remote context.

    One could argue that something like a printing service should be accessed locally! But in that case the request for the service should be local (method call) and there is no reason to send an instance! (Only possible exceptions are those of point 3).

    Note: the class Service above is not suited for this remote reference passing since it will get its behaviour only from the static context of the class. (But you can design hybrid codes with local and remote data).

Point 3: singletons as special instances of a service

An example of this is Collections.EMPTY_LIST we have a singleton that implements the List services. In fact this looks like an enum with a single value.

Since those could be elements in a structure (an object has a reference to a List which happens to be an EMPTY_LIST) they should be serialised.

Then if you want to use checks such as node == EMPTY_LIST then the unicity of the instance is required (and readResolve used).

May happen! ..... so what?

In my humble opinion the systematic use of readResolve is over-engineering: we are afraid of instance duplication .... should we be afraid of that?

For sure I am interested in examples of singletons that do not match the abovementioned points that will show that it is hard to prove a general point and when confronted with a suspicion of over-engineering there are decisions that rely on risk analysis (we do not have an expert at hand everytime).

(Again the example is a bit far-fetched: there is no big risk involved here).

A side note: this blog entry was written in response to this blog but the editorial process there failed to register a link to this response ... so ?

1 comment:

control valves said...

Thanks for the helpful information. Hope to hear more from you.

About Me

My photo
to graduate at my architecture's school I wrote a thesis on "fuzzy" methods (that was in 1974). afterwards I turned software engineer (and later to java evangelist) but this just strenghtened my views on methods ... I'll try to share (though it is hard to write precisely on fuzzy topics). ...