Chapter 6. The Singleton pattern

In this chapter, we'll take up the Singleton pattern. This pattern is grouped with the other Creational patterns, although it is to some extent a pattern that limits, rather than promotes, the creation of classes. Specifically, it ensures that there is one and only one instance of a class and provides a global point of access to that instance. Any number of cases in programming in which you need to ensure that there can be one and only one instance of a class are possible. For example, your system might have only one window manager or print spooler or one point of access to a database engine. Or, your computer might have several serial ports, but there can only be one instance of COM1.

Creating a Singleton Using a Static Method

The easiest way to create a class that can have only one instance is to embed a static variable inside of the class that is set on the first instance and then check for it each time that you enter the constructor. A static variable is a variable for which there is only one instance, no matter how many instances of the class exist. To prevent instantiating the class more than once, make the constructor private so that an instance can be created only from within the static method of the class. Then we create a method called Instance, which will return an instance of Spooler, or null, if the class has already been instantiated. This is illustrated in the following code:

public class PrintSpooler {
    private static PrintSpooler spooler;

    private PrintSpooler() {
    }
  //return only one spooler instance
    public static synchronized PrintSpooler getSpooler() {
        if (spooler == null)              //if none created
            spooler = new PrintSpooler(); //create one
        return spooler;                   //return it
    }
    public void print(String s) {
        System.out.println(s);
    }
}

This approach has the major advantage that you don't have to worry about exception handling if the Singleton already exists—you always get the same instance of the spooler.

And, should you try to create instances of the PrintSpooler class directly, creating instances will fail at compile time because the constructor has been declared as private.

    //fails at compile time because constructor is privatized
    PrintSpooler pr3 = new PrintSpooler();

Finally, should you need to change the program to allow two or three instances, this class is easily modified to allow this.

If instead you want to know whether a spooler has been created and that you cannot create another, you can return a null if the spooler already exists, or you can throw an exception.

Exceptions and Instances

If you want to know whether you have created a new instance, you must check the getSpooler method return to make sure it is not null. Assuming that programmers will always remember to check for errors is the beginning of a slippery slope that many prefer to avoid.

Instead, you can create a class that throws an exception if you attempt to instantiate it more than once. This requires the programmer to take action and is thus a safer approach.

Let's create our own exception class for this case.

public class SingletonException extends RuntimeException {
    //new exception type for singleton classes
    public SingletonException() {
        super();
    }
    public SingletonException(String s) {
        super(s);
    }
}

Note that other than calling its parent classes through the super method, this new exception type doesn't do anything in particular. However, it is convenient to have our own named exception type so that the compiler will warn us of the type of exception we must catch when we attempt to create an instance of PrintSpooler.

Throwing an Exception

Let's write the skeleton of our Spooler class—we'll omit all of the printing methods and just concentrate on correctly implementing the Singleton pattern:

public class Spooler {
    //this is a prototype for a printer-spooler class
    //such that only one instance can ever exist
    static boolean instance_flag = false;    //true if one instance

    public Spooler() throws SingletonException
    {
       if(instance_flag)
           throw new SingletonException("Only one printer
           allowed");
       else
           instance_flag = true;        //set flag for one instance
        System.out.println("printer opened");
    }
}

Creating an Instance of the Class

Now that we've created our simple Singleton pattern in the PrintSpooler class, let's see how we use it. Remember that we must enclose every method that may throw an exception in a try-catch block.

public class singleSpooler
{
    static public void main(String argv[])
    {
        Spooler pr1, pr2;
        //open one printer--should always work
        System.out.println("Opening one spooler");
        try {
        pr1 = new Spooler();
        }
        catch (SingletonException e)
        (System.out.println(e.getMessage());}
        //try to open another printer--should fail
        System.out.println("Opening two spoolers");
        try {
        pr2 = new Spooler();
        }
        catch (SingletonException e)
        {System.out.println(e.getMessage());}
    }
}

If we execute this program, we get the following results:

Opening one spooler
printer opened
Opening two spoolers
Only one spooler allowed

The last line indicates that an exception was thrown as expected. The complete source of this program is on the example CD-ROM as singleSpooler.java.

Providing a Global Point of Access to a Singleton Pattern

Since a Singleton pattern is used to provide a single point of global access to a class, your program design must provide for a way to reference the Singleton throughout the program, even though Java has no global variables.

One solution is to create such Singletons at the beginning of the program and pass them as arguments to the major classes that might need to use them.

pr1 = new Spooler();
Customers cust = new Customers (pr1);

The disadvantage is that you might not need all of the Singletons that you create for a given program execution. This could have performance implications. A more elaborate solution is to create a registry of all of the program's Singleton classes and make the registry generally available. Each time a Singleton is instantiated, it notes that in the registry. Then any part of the program can ask for the instance of any Singleton using an identifying string and get back that instance variable.

The disadvantage of the registry approach is that type checking might be reduced, since the table of Singletons in the registry probably keeps all of the Singletons as objects, for example in a Hashtable object. And, of course, the registry itself is probably a Singleton and must be passed to all parts of the program using the constructor of various set functions.

Probably the most common way to provide a global point of access is by using static methods of a class. The class name is always available, and the static methods can be called only from the class and not from its instances, so there is never more than one such instance no matter how many places in your program call that method. This is the approach used in the Java serial port package, javax.comm, discussed next.

The javax.comm Package as a Singleton

The javax.comm package is provided separately from the Java Software Development Kit (SDK) and is downloadable from the Sun Web site. This package is designed to provide control over the serial and parallel ports of a computer. Each supported platform has a different implementation of this package, since it must include native code to handle the actual system hardware.

Serial ports are a good example of a resource that should be represented by a Singleton, since only one program at a time can access a serial port, and even within a program only one module or group of modules should be communicating over the port.

Actually, two Singletons are possible here: one that manages the collection of ports and lets out only one instance of each port and one that manages the port objects themselves, which must each refer to a single port.

In the javax.comm package, the CommPortIdentifier class has a static method for returning an enumeration of all ports in the system:

public static Enumeration getPortIdentifiers()

This enumeration contains a CommPortIdentifier object for each element in the system. This package handles both parallel and serial ports, so you also need to filter the returned objects to allow only the serial ports.

//get enumeration of ports--static method
portEnum = CommPortIdentifier.getPortIdentifiers();

while (portEnum.hasMoreElements()) {
   portId = (CommPortIdentifier) portEnum.nextElement();
   if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
       ports.addElement (portId.getName()); //add to list
}

The getPortIdentifiers method is static, so it provides the single global point of access required by the Singleton definition. Further, since it is a static method, multiple instances are not allowed. You can call this method only from the class and not from its instances. Each CommPortIdentifier instance returned from this enumeration has a distinct port name, which you can access using the getName method.

This enumeration consists of all possible ports for the system, whether opened or not, implemented or not. For example, on a PC the enumeration returns COM1, COM2, COM3, and COM4, regardless of whether cards for all of these ports are installed.

The only way you can make sure that a port is available is only by attempting to open it. If the port doesn't exist at all, the open method throws the NoSuchPortException; if the port is in use by another program, it throws the PortInUseException. The code for handling this looks like the following:

    //try to get port ownership
    portId = CommPortIdentifier.getPortIdentifier(portName);

    //if successful, open the port
        Commport cp = portId.open("SimpleComm",100);
   //report success
        status.add("Port opened: "+portName);
    }
    catch(NoSuchPortException e){
        status.add("No such port:"+portName);
    }
    catch(PortInUseException e) {
        status.add (portName+" in use by: " +
                     portId.getCurrent Owner());
}

Note that we are sending the error messages to some sort of status display. The important thing is that the CommPort cp is non-null only if no exceptions are thrown. We illustrate the use of these ports in the SimpleComm program shown in Figure 6.1.

The SimpleComm program executing, showing how it represents ports that do not exist and those that are not owned.

Figure 6.1. The SimpleComm program executing, showing how it represents ports that do not exist and those that are not owned.

An enumeration of the four legal ports that the PC BIOS recognizes is shown on the left. In addition, we added the bogus COM5 port to show the error message it generates. Clicking on each port in turn opens that port and assigns it to the SimpleComm program. When we click on COM3 and COM4, the system throws the PortInUseException, as if ports 3 and 4 were assigned to the system and in use by it. However, the getCurrentOwner method returns the message "Port currently not owned," which means it really didn't exist in the first place. When we click on COM5, we get the NoSuchPortException. When we again click on COM2, we find that it is now in use as well, and by this program.

You might ask whether each port is a Singleton. There can be many instances of a port, but you can have only one open at a time. This certainly is providing a single global point of access and making sure that only one can be used at a time. Thus we can probably view the opened ports as Singletons as well.

Building a CommPortManager

Let's consider constructing an enclosing class for these Singleton methods and calling it a PortManager. We expect the class to allow us to enumerate the available ports and open any of them. Some of the methods in such a class might be as follows:

public abstract class PortManager {
public static Enumeration  getAllPorts();
public static Enumeration  getAvailablePorts();
public static CommPort     openPort(String portName);
public static CommPort     openPort();
}

Because of the Java syntax rules, we can't have abstract static classes, so we actually declare each of them in the parent abstract class to return null.

The getAllPorts method is just like the one shown previously, when we enumerated the ports. However, the getAvailablePorts method is interesting because we don't need to do anything at all when we catch the port exceptions. We get the port and try to open it. If neither operation fails, we add the port name to the list of available ports.

public static Enumeration getAvailablePorts() {
    Vector portOpenList = new Vector();
    CommPortIdentifier portId;

    Enumeration enum = getAllPorts();
    while (enum.hasMoreElements ()) {
        String portName = (String) enum.nextElement () ;
        try {
            //try to get port ownership
            portId =
                CommPortIdentifier.getPortIdentifier(portName);
            //if successful, open the port
            CommPort cp = portId.open("SimpleComm",100);
            //report success
            portOpenList.addElement(portName);
            cp.close();
            }
        catch(NoSuchPortException e){}
        catch(PortInUseException e){}
        }
    return portOpenList.elements ();
}

The getAvailablePorts method is illustrated in Figure 6.2.

The available ports assigned and then in use.

Figure 6.2. The available ports assigned and then in use.

Which are the Singletons here? We really just wrapped the CommPortIdentifier methods so that we could handle the exceptions internally. The static CommPortIdentifier method might be considered a Singleton registry, since it provides access to four or more different ports. However, as a static method it does provide the single point of access. The ports themselves remain the Singletons, since only one instance of an opened port can exist.

Other Consequences of the Singleton Pattern

The Singleton Pattern has the following additional consequences:

  1. Subclassing a Singleton can be difficult, since this can work only if the base Singleton class has not yet been instantiated.

  2. You can easily change a Singleton to allow a small number of instances, where this is allowed and meaningful.

Thought Question

  1. Consider a system for processing ATM card transactions. A thief is using a stolen ATM card number to steal funds, concurrent with the legitimate user's withdrawing funds. How could you design a Singleton to reduce this risk?

Programs on the CD-ROM

Program Description

SingletonsingleSpoolerSingleSpooler.java

Shows how to write a print spooler throwing an exception.

SingletonfinalSpoolfinalspool.java

Returns a single instance of a spooler and will not create more.

SingletonInstanceSpoolerInstanceSpooler.java

Creates one instance or returns null.

SingletonCommSimpleComm.java

Simple illustration of Comm class uses.

SingletonPortManagerManageComm.Java

Uses the CommPortManager class.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset