n class instances

Limit class instances with a modified singleton

November 2, 2001

Q: How can I write a class such that I can create only five instances of that class?

A: In both “Singletons Rule” and “Effective Object-Oriented Design,” I covered the Singleton design pattern, which defines a class so that you can instantiate only one object of that class.

Your goal is a little different. Instead of limiting the number of instantiations to one object, you would like to set that limit to some arbitrary n number of objects. In your case, you would like to set n to 5 objects. With the standard singleton pattern, you would say that n is 1.

A design pattern presents a general design for an abstracted problem. For that reason it is often valid to alter a general design pattern to fit your concrete problem. In this case, you should alter the Singleton pattern so that it allows a limited number of instances for a class. (See note at the end of this article.) Here’s the code for a standard Singleton class:

public class Singleton 
{
    
    private static final Singleton instance = new Singleton();
    
    public static Singleton instance()
    {
        return instance;
    }
    
    // any public instance methods go here
    
    private Singleton() 
    {
        // prevent direct instantiation
    }
}

A Java singleton implementation normally makes the singleton instance available through a public final static class variable (as in the code above) or through a static factory method.

An n singleton implementation could look like:

public class NSingleton 
{
    public static final NSingleton INSTANCE_1 = new NSingleton();
    public static final NSingleton INSTANCE_2 = new NSingleton();
    public static final NSingleton INSTANCE_3 = new NSingleton();
    public static final NSingleton INSTANCE_4 = new NSingleton();
    public static final NSingleton INSTANCE_5 = new NSingleton();
    
    // any public instance methods go here
    
    private NSingleton() 
    {
        // prevent direct instantiation
    }
}

Like the aforementioned standard Singleton class, NSingleton makes the five instances available through five public class variables.

You don’t state the rationale for limiting the instantiations to five. Perhaps you desire the capability to load balance requests to some back-end service — connection pooling maybe? In such a case you could alter the pattern further so that you can encapsulate exactly which instance another object may use:

public class AlternateSingleton 
{
    private final static SingletonIterator instances = new SingletonIterator();
    
    public static AlternateSingleton instance()
    {
        return instances.next();
    }
    // any public instance methods go here
        
    private AlternateSingleton() 
    {
        // prevent direct instantiation
    }
    // encapsulate the logic for looping over and holding onto the singletons
    private static class SingletonIterator
    {
        private AlternateSingleton [] values;
        private int counter;
        
        public SingletonIterator()
        {
            values[0] = new AlternateSingleton();
            values[1] = new AlternateSingleton();
            values[2] = new AlternateSingleton();
            values[3] = new AlternateSingleton();
            values[4] = new AlternateSingleton();
        }
        
        // unlike a standard iterator, this iterator will continually loop over
        // all of the contained elements
        public AlternateSingleton next()
        {
            if( counter == 5 )
            {
                counter = 0;
            }
            AlternateSingleton instance = values[counter];
            counter++;
            return instance;
        }
    }
    
}

AlternateSingleton brokers access through the static factory method instance() rather than making the instances directly accessible through public class variables. By encapsulating the instances, you can control how other objects access them.

In this example, the AlternateSingleton load balances requests among all instances. In object-oriented programming, it’s important to properly divide responsibility. In this case AlternateSingleton removes the load balancing responsibility from the instance users and puts it into the singleton itself (and the singleton further delegates the responsibility to an inner class). Now, instead of each user having to worry about load balancing their requests, that responsibility is isolated within one place.

Note: The skill to know when and when not to alter a pattern comes with experience. When deciding whether or not to deviate from a pattern, be sure you have a valid reason for the deviation.

Indeed, don’t automatically change a pattern just to fit your design — you might fall into a trap. Instead, look first at your design, and ask yourself if it is truly valid.

Tony Sintes is an
independent consultant and founder of First Class Consulting,
Inc., a consulting firm that specializes in the bridging of
disparate enterprise systems and training. Outside of First Class
Consulting, Tony is an active freelance writer as well as author of
Sams Teach Yourself Object-Oriented Programming
in 21 Days (Sams, 2001; ISBN: 0672321092).

Source: www.infoworld.com