Hitchhiker's Guide to Software Architecture and Everything Else - by Michael Stal

Sunday, January 28, 2007

The Singleton Patterns and The Concept of Identity

During my Java EE 5 tutorial at the TOOP 2007 conference, an attendee asked how the Singleton pattern should be best leveraged in Java EE. In my opinion the Singleton pattern is more a workaround than a proven solution and thus it is seldomly recommendable to apply it.
Basically, what the Singleton does is to guarantee the existence of one single instance of a class.

Example from JavaEE:

// get ConnectionFactory via JNDI
ConnectionFactory cf = ... // single instance
Connection c = cf.CreateConnection(...);

The factory uses the Singleton pattern such as in:

class ConnectionFactory {
static private ConnectionFactory _instance = null;
protected ConnectionFactory() { ... };
static public ConnectionFactory instance() {
if (null == _instance)
_instance = new ConnectionFactory();
return _instance;
}
}

For sake of brevity I didn't care about thread safety. For our purpose you can just ignore this fact. In the code you will recognize that no external user of the class can access the protected constructor so that the only way to obtain an instance is calling the static factory method instance().
This way, there will be exactly one instance whenever one or more clients access the class.

There are many problems with this approach. The most important one is that a Singleton defines a global variable such as ConnectionFactory.instance().

Suppose, we need exactly one object instance. Do we really need to rely on Singleton?
Let me introduce a simple example.


class ThePrinter {
private String printerAddress;
public void print(String fileName) { ... }
public ThePrinter() { printerAddress = MY_CONSTANT_VALUE; }
}

In the class above, we can provide multiple instances and each of these instances will semantically define the same object (thus we should provide appropriate equals and hashCode contracts).

From an identity viewpoint, we need to constrain our class to provide objects with the same identity. The way a Singleton tries to guarantee this is by only providing one single instance. But it is legal and much more appropriate (in terms of OO pureness) to achieve the same result using an immutable class as shown in the printer example above.

Another option of course would be if we need to constrain the numbers or kinds of instances created by a class such as in a Manager or Pool class. These only allow creating specific instances or a maximum number of instances. Thread and connection pools are typical examples for such behavior. In this context, a Singleton is a Pool with capacity == 1. I won't show you more code here. You can google for Peter Sommerlad's Manager pattern.

Another "inverse" kind of approach in terms of identity is the following. Suppose, you got thousands of users on your Web site. You started with providing one volatile user object for each online user which made your system less fault-tolerant and more resource consuming. How could you change this? In other words, how could you provide a limited number of instances of user objects (# user objects << # online users)?
a) implement a pool of fixed user objects that wait for incoming requests (Active Object pattern)
b) when a request comes in, it is enqueued
c) one of the idle objects dequeues the request
d) it retrieves the user id from the request and looks up the user's data in the database
e) it operates on the user data and stores it back to the database
f) the result is returned to the client
g) the user object is ready to take another request and thus another identity

This approach does even work if multiple user objects run under the same user identity. In this context, each pool object can temporarily take any identity because the identity's data itself is persisted somewhere else. Thus, identity is not the same as instance.

Keep this in mind when you next time feel addicted to solve an identity problem with the Singleton pattern or other weak approaches.

It is surprising how often architects and engineers forget to think about identity and how to design and implement it.

1 Comments:

  • I agree with your assessment that the Singleton pattern is a workaround that should almost always be avoided.

    I just wanted to add another contra argument, and this is coupling. By using a singleton, you couple your code to the class of the singleton object. This is a problem in itself, but is amplified by the fact that singletons are often used in a context where they provide a service of some sort (connection factory for example).

    Clients of this service are very hard to unit test, since it is not easily possible to uncouple the client from the implementation of the service.

    You will have to configure the clients (or even the singleton) to use alternative implementations (if the singleton implements an interface) or worse, a subclass, which will lead to an even greater mess.

    By Anonymous Anonymous, at 12:30 AM  

Post a Comment

<< Home