Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton with subclassing in java

The most common way of implementing a singleton in java is to use a private constructor with a public accessor method of the form --

public class Singleton {
    private static Singleton instance = null;
    private Singleton() {

    }
    public static synchronized Singleton getInstance(){
        if (instance == null) {
        instance = new Singleton();         
        } 
        return instance;
    }
}

However, since the constructor is private, it prevents subclassing the singleton. Is there any way in which we can make a singleton which allows subclassing ?

like image 630
user496934 Avatar asked Dec 18 '11 04:12

user496934


People also ask

Can a singleton class have subclasses?

Subclassing a Singleton class may be tricky, since a subclass object cannot be created unless the superclass object has not yet been created. You may want to extend the Singleton class to allow not just a single instance, but some small fixed maximum number of instances.

Can singleton be garbage collected?

Singleton class has a static reference to the instantiated singleton object and hence it will never be garbage collected unless ofcourse as Jon Skeet as stated that the context that loaded this class (class loader) is itself eligible for garbage collection in which case that static reference will not longer be a GC ...

What is the best way to subclass singleton?

I would argue the most common way to implement a singleton is to use an enum with one instance. That might be a "better" way but definitely not the most common way. In all the projects I have worked on, Singletons are implemented as I have shown above.

What is subclassing in Java?

In the Java language, classes can be derived from other classes, thereby inheriting fields and methods from those classes. Definitions: A class that is derived from another class is called a subclass (also a derived class, extended class, or child class).


2 Answers

I respectfully offer a counterpoint to the comments that suggest a singleton should not be subclassed. Subclassing a singleton is discussed in "Design Patterns: Elements of Reusable Object-Oriented Software" by Gamma, Helm, Johnson, and Vlissides (aka "The Gang of Four" book or "GOF" for short).

A properly subclassed singleton would also be a singleton. For example, suppose you have a singleton that handles logging informational messages called Logger. Now suppose you want to extend the functionality of Logger to write output using HTML tags. Let's call it HTMLLogger. Both of these classes are singletons, but one extends the functionality of the other.

First, here's a simple singleton and its test case:

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.simple;

public class SimpleSingleton {
    // The instance - only one of these can exist in the system (currently not accounting for threads).
    private static SimpleSingleton instance;

    private int sampleValue;

    public static SimpleSingleton getInstance() {
        if (instance == null) {
            instance = new SimpleSingleton();               
        }
        return instance;
    }

    public int getSampleValue() {
        return sampleValue;
    }

    public void setSampleValue(int sampleValue) {
        this.sampleValue = sampleValue;
    }

    protected SimpleSingleton() {
        // Insures construction cannot occur outside of class.
    }
}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.simple.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import study.design.patterns.creational.singleton.simple.SimpleSingleton;

public class SimpleSingletonTest {

    @Test
    public void testIllegalCreation() {
        // The following line will not compile because the constructor is not visible.
        // Singleton instance = new Singleton();        
    }

    @Test
    public void testObjectEquality() {
        SimpleSingleton instance1 = SimpleSingleton.getInstance();
        assertNotNull(instance1);
        SimpleSingleton instance2 = SimpleSingleton.getInstance();
        assertNotNull(instance2);
        assertEquals(instance1, instance2);     
    }

    @Test
    public void testDataEquality() {
        SimpleSingleton instance1 = SimpleSingleton.getInstance();
        assertNotNull(instance1);
        SimpleSingleton instance2 = SimpleSingleton.getInstance();
        assertNotNull(instance2);
        assertEquals(instance1, instance2);
        instance1.setSampleValue(5);
        int testSampleValue = instance2.getSampleValue();
        assertEquals(testSampleValue, 5);
    }
}

/////////////////////////////////////////////////////////////////////////////

I've found four ways you can subclass a singleton.

Option 1. Brute force.

Essentially, the subclass reimplements the key features to make the class a singleton. That is, the static instance variable, the static getInstance method, and a hidden constructor. In this case, the hidden constructor calls the base class.

Here's a sample base class, subclass, and test case:

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassbruteforce;

// This singleton can be extended (subclassed)
public class BruteForceExtendableSingleton {
    // The instance - only one of these can exist in the system (currently not accounting for threads).
    private static BruteForceExtendableSingleton instance;

    private int sampleValue;

    public static BruteForceExtendableSingleton getInstance() {
        // The problem with this version of an extendable singleton is clear from the code below - every subclass possible is hard-coded.
        // Creating a new subclass requires modifying the base class as well, which violates the open-closed principle.
        if (instance == null) {
            instance = new BruteForceExtendableSingleton();
        }
        return instance;
    }

    public int getSampleValue() {
        return sampleValue;
    }

    public void setSampleValue(int sampleValue) {
        this.sampleValue = sampleValue;
    }

    protected BruteForceExtendableSingleton() {
        // Insures construction cannot occur outside of class.
    }
}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassbruteforce;

public class BruteForceSubclassSingleton extends BruteForceExtendableSingleton {
    // The instance - only one of these can exist in the system (currently not accounting for threads).
    private static BruteForceSubclassSingleton instance;

    private int sampleValue2;

    public static BruteForceSubclassSingleton getInstance() {
        // The problem with this version of an extendable singleton is clear from the code below - every subclass possible is hard-coded.
        // Creating a new subclass requires modifying the base class as well, which violates the open-closed principle.
        if (instance == null) {
            instance = new BruteForceSubclassSingleton();
        }
        return instance;
    }

    public int getSampleValue2() {
        return sampleValue2;
    }

    public void setSampleValue2(int sampleValue2) {
        this.sampleValue2 = sampleValue2;
    }

    protected BruteForceSubclassSingleton() {
        super();
    }
}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassbruteforce.test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

import study.design.patterns.creational.singleton.subclassbruteforce.BruteForceExtendableSingleton;
import study.design.patterns.creational.singleton.subclassbruteforce.BruteForceSubclassSingleton;

public class BruteForceExtendableSingletonTest {

    @Test
    public void testIllegalCreation() {
        // The following lines will not compile because the constructor is not visible.
        // BruteForceExtendableSingleton instance = new BruteForceExtendableSingleton();        
        // BruteForceSubclassSingleton instance2 = new BruteForceSubclassSingleton();
    }

    @Test
    public void testCreateBruteForceExtendableSingleton() {
        BruteForceExtendableSingleton singleton = BruteForceExtendableSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is an ExtendableSingleton, but not a FixedSubclassSingleton. 
        assertTrue(singleton instanceof BruteForceExtendableSingleton);
        assertFalse(singleton instanceof BruteForceSubclassSingleton);      
    }

    @Test
    public void testCreateBruteForceSubclassSingleton() {
        BruteForceExtendableSingleton singleton = BruteForceSubclassSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is a BruteForceSubclassSingleton. 
        assertTrue(singleton instanceof BruteForceSubclassSingleton);
    }

    @Test
    public void testCreateBothBruteForceSingletons() {
        BruteForceExtendableSingleton singleton = BruteForceExtendableSingleton.getInstance();
        assertNotNull(singleton);
        assertTrue(singleton instanceof BruteForceExtendableSingleton);
        assertFalse(singleton instanceof BruteForceSubclassSingleton);

        BruteForceExtendableSingleton singleton2 = BruteForceSubclassSingleton.getInstance();
        assertNotNull(singleton2);
        assertTrue(singleton2 instanceof BruteForceSubclassSingleton);

        assertFalse(singleton == singleton2);
    }   
}

/////////////////////////////////////////////////////////////////////////////

Pros: Allows for both singletons to exist at the same time.

Cons: Duplication of effort to create a singleton. The singleton nature of the subclass does not come from its base class.

If the singletons need to be separate, it's possible that a better design is needed to share the other methods instead of subclassing.

Option 2. Selecting from a fixed set of classes.

In this case, the getInstance method in the base class determines which instance to use based on a flag, such as a system property. In the code sample, I use the name of the class itself. Using a series of if blocks, the code decides how to initialize the instance.

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassfixed;

// This singleton can be extended (subclassed)
public class FixedExtendableSingleton {
    // The instance - only one of these can exist in the system (currently not accounting for threads).
    private static FixedExtendableSingleton instance;

    private int sampleValue;

    public static FixedExtendableSingleton getInstance() {
        // The problem with this version of an extendable singleton is clear from the code below - every subclass possible is hard-coded.
        // Creating a new subclass requires modifying the base class as well, which violates the open-closed principle.
        if (instance == null) {
            String singletonName = System.getProperty("study.design.patterns.creational.singleton.classname");
            if (singletonName.equals(FixedExtendableSingleton.class.getSimpleName())) {
                instance = new FixedExtendableSingleton();
            } else if (singletonName.equals(FixedSubclassSingleton.class.getSimpleName())) {
                instance = new FixedSubclassSingleton();                
            }
        }
        return instance;
    }

    public static void clearInstance() {
        // This method wipes out the singleton. 
        // This is purely for testing purposes so getInstance can reconnect to a new singleton if needed.
        instance = null;
    }

    public int getSampleValue() {
        return sampleValue;
    }

    public void setSampleValue(int sampleValue) {
        this.sampleValue = sampleValue;
    }

    protected FixedExtendableSingleton() {
        // Insures construction cannot occur outside of class.
    }
}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassfixed;

public class FixedSubclassSingleton extends FixedExtendableSingleton {

    private int sampleValue2;

    public int getSampleValue2() {
        return sampleValue2;
    }

    public void setSampleValue2(int sampleValue2) {
        this.sampleValue2 = sampleValue2;
    }

    // Must be defined to prevent creation of a public default constructor. 
    protected FixedSubclassSingleton() {
        super();
    }
}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassfixed.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import study.design.patterns.creational.singleton.subclassfixed.FixedExtendableSingleton;
import study.design.patterns.creational.singleton.subclassfixed.FixedSubclassSingleton;

public class FixedExtendableSingletonTest {

    @Test
    public void testIllegalCreation() {
        // The following lines will not compile because the constructor is not visible.
        // ExtendableSingleton instance = new ExtendableSingleton();        
        // FixedSubclassSingleton instance = new FixedSubclassSingleton();
    }

    @Test
    public void testCreateExtendableSingleton() {
        System.setProperty("study.design.patterns.creational.singleton.classname", "FixedExtendableSingleton");
        FixedExtendableSingleton singleton = FixedExtendableSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is an ExtendableSingleton, but not a FixedSubclassSingleton. 
        assertTrue(singleton instanceof FixedExtendableSingleton);
        assertFalse(singleton instanceof FixedSubclassSingleton);

    }

    @Test
    public void testCreateFixedSubclassSingleton() {
        System.setProperty("study.design.patterns.creational.singleton.classname", "FixedSubclassSingleton");
        FixedExtendableSingleton singleton = FixedExtendableSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is a FixedSubclassSingleton. 
        assertTrue(singleton instanceof FixedSubclassSingleton);
    }

    @AfterEach
    protected void tearDown() {
        FixedExtendableSingleton.clearInstance();
    }   
}

/////////////////////////////////////////////////////////////////////////////

Pros: Clearer binding of subclass to singleton behavior. Reduction of duplicate code.

Cons: Only a fixed set of subclasses are defined. Adding a new subclass requires modifying the getInstance method.

Option 3. Determine which singleton to use from a dynamic set of classes.

This method attempts to remove the need to modify getInstance for every subclass. The idea is to include a registry (map) of names to singletons in the base class, and look up the correct one in getInstance.

In order to populate the registry with singletons, each singleton needs to be created beforehand. How is this done? According to GOF, we can assign a static variable to an instance of the object. When the class is loaded, the singleton is constructed, and the constructor adds the object to the registry. This is more complex, but it works (sort of).

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassflexible;

import java.util.HashMap;
import java.util.Map;

//This singleton can be extended (subclassed)
public class FlexibleExtendableSingleton {
    // The instance - only one of these can exist in the system (currently not accounting for threads).
    private static FlexibleExtendableSingleton instance;

    // This must appear before thisSingleton, because the constructor requires the registry.
    protected static Map<String, FlexibleExtendableSingleton> registry = new HashMap<String, FlexibleExtendableSingleton>();

    // This singleton - each class in the hierarchy needs one of these. It will trigger construction (and therefore, registration).
    private static FlexibleExtendableSingleton thisSingleton = new FlexibleExtendableSingleton();

    public static void activateClass() {
        // Do nothing special.
    }

    private int sampleValue;

    protected static void register(String name, FlexibleExtendableSingleton singletonClass) {
        registry.put(name, singletonClass); 
    }

    protected static FlexibleExtendableSingleton lookupFromRegistry(String name) {
        return registry.get(name);
    }

    public static FlexibleExtendableSingleton getInstance() {
        if (instance == null) {
            String singletonName = System.getProperty("study.design.patterns.creational.singleton.classname");
            instance = lookupFromRegistry(singletonName);
        }
        return instance;
    }

    public static void clearInstance() {
        // This method wipes out the singleton. 
        // This is purely for testing purposes so getInstance can reconnect to a new singleton if needed.
        instance = null;
    }

    public int getSampleValue() {
        return sampleValue;
    }

    public void setSampleValue(int sampleValue) {
        this.sampleValue = sampleValue;
    }

    protected FlexibleExtendableSingleton() {
        // Protected insures construction cannot occur outside of class.

        // Register the class when it is constructed by its static method.
        // Subclasses will be able to use this method as well.
        register(this.getClass().getSimpleName(), this);
    }

}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassflexible;

import study.design.patterns.creational.singleton.subclassdynamicload.DynamicLoadExtendableSingleton;

public class FlexibleSubclassSingleton extends FlexibleExtendableSingleton {
    // This singleton - each class in the hierarchy needs one of these. It will trigger construction (and therefore, registration).
    private static FlexibleSubclassSingleton thisSingleton = new FlexibleSubclassSingleton();

    private int sampleValue2;

    public static void activateClass() {
        // Do nothing special.
    }

    public int getSampleValue2() {
        return sampleValue2;
    }

    public void setSampleValue2(int sampleValue2) {
        this.sampleValue2 = sampleValue2;
    }

    // Must be defined to prevent creation of a public default constructor. 
    protected FlexibleSubclassSingleton() {
        // The following line will also register the class.
        super();
    }
}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassflexible.test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import study.design.patterns.creational.singleton.subclassflexible.FlexibleExtendableSingleton;
import study.design.patterns.creational.singleton.subclassflexible.FlexibleSubclassSingleton;

public class FlexibleExtendableSingletonTest {

    @Test
    public void testIllegalCreation() {
        // The following lines will not compile because the constructor is not visible.
        // FlexibleExtendableSingleton instance = new FlexibleExtendableSingleton();        
        // FlexibleSubclassSingleton instance2 = new FlexibleSubclassSingleton();
    }

    @Test
    public void testCreateFlexibleExtendableSingleton() {
        System.setProperty("study.design.patterns.creational.singleton.classname", "FlexibleExtendableSingleton");
        FlexibleExtendableSingleton.activateClass();
        FlexibleSubclassSingleton.activateClass();      
        FlexibleExtendableSingleton singleton = FlexibleExtendableSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is an ExtendableSingleton, but not a FixedSubclassSingleton. 
        assertTrue(singleton instanceof FlexibleExtendableSingleton);
        assertFalse(singleton instanceof FlexibleSubclassSingleton);        
    }

    @Test
    public void testCreateFlexibleSubclassSingleton() {
        System.setProperty("study.design.patterns.creational.singleton.classname", "FlexibleSubclassSingleton");
        FlexibleExtendableSingleton.activateClass();
        FlexibleSubclassSingleton.activateClass();      
        FlexibleExtendableSingleton singleton = FlexibleExtendableSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is a FlexibleSubclassSingleton. 
        assertTrue(singleton instanceof FlexibleSubclassSingleton);
    }

    @AfterEach
    protected void tearDown() {
        FlexibleExtendableSingleton.clearInstance();
    }   
}

/////////////////////////////////////////////////////////////////////////////

Notice the method "activateClass" in each of the singletons. This method is empty and appears to do nothing. In reality, it is there to trigger loading the class for the first time. When called, the class is loaded, which creates the static singleton instance, which adds the entry to the registry. If the class is not loaded, the registry will not be populated and getInstance will return null for any class except for the base class, because calling getInstance will also trigger loading the base class.

Alternatively, instead of using "activateClass" methods, you could use a ClassLoader to load all of the singleton classes. You would still need to explicitly load every singleton class.

Pros: getInstance does not have to be modified each time.

Cons: Every subclass requires an empty activateClass method (or another way to load the class), which must be called prior to getInstance. Since each singleton class must be activated, we didn't gain much improvement from Option 2.

Option 4. Dynamically loading the singleton by name.

In Option 3 above, we had the problem of loading the singleton classes to populate a registry. Since the selection of the singleton is already being controlled by a system property, why not just load the singleton class to be used and set that as the instance?

Using reflection, we can load the class by name, locate the static singleton (the "thisSingleton" field), and assign it to the instance.

NOTE: Reflection enables a developer to bypass encapsulation, so should be used with caution. In this case, its use is limited to getInstance.

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassdynamicload;

import java.lang.reflect.Field;

//This singleton can be extended (subclassed)
public class DynamicLoadExtendableSingleton {
    // The instance - only one of these can exist in the system (currently not accounting for threads).
    private static DynamicLoadExtendableSingleton instance;

    // This singleton - each class in the hierarchy needs one of these. It will trigger construction (and therefore, registration).
    private static DynamicLoadExtendableSingleton thisSingleton = new DynamicLoadExtendableSingleton();

    private int sampleValue;

    public static DynamicLoadExtendableSingleton getInstance() {
        if (instance == null) {
            String singletonName = System.getProperty("study.design.patterns.creational.singleton.classname");

            ClassLoader loader = DynamicLoadExtendableSingleton.class.getClassLoader();
            try {
                Class<?> singletonClass = loader.loadClass(singletonName);
                Field field = singletonClass.getDeclaredField("thisSingleton");
                field.setAccessible(true);
                instance = (DynamicLoadExtendableSingleton) field.get(null);
            } catch (ClassNotFoundException e) {
                // The class was not found.
                // TODO: Add error handling code here.
            } catch (NoSuchFieldException e) {
                // The field does not exist - fix the singleton class to include thisSingleton field.
                // TODO: Add error handling code here.
            } catch (IllegalAccessException e) {
                // Should not occur - we make the field accessible just for this purpose.
                // TODO: Add error handling code here.
            }
        }
        return instance;
    }

    public static void clearInstance() {
        // This method wipes out the singleton. 
        // This is purely for testing purposes so getInstance can reconnect to a new singleton if needed.
        instance = null;
    }

    public int getSampleValue() {
        return sampleValue;
    }

    public void setSampleValue(int sampleValue) {
        this.sampleValue = sampleValue;
    }

    protected DynamicLoadExtendableSingleton() {
        // Protected insures construction cannot occur outside of class.
    }

}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassdynamicload;

public class DynamicLoadSubclassSingleton extends DynamicLoadExtendableSingleton {
    // This singleton - each class in the hierarchy needs one of these. It will trigger construction (and therefore, registration).
    private static DynamicLoadSubclassSingleton thisSingleton = new DynamicLoadSubclassSingleton();

    private int sampleValue2;

    public int getSampleValue2() {
        return sampleValue2;
    }

    public void setSampleValue2(int sampleValue2) {
        this.sampleValue2 = sampleValue2;
    }

    // Must be defined to prevent creation of a public default constructor. 
    protected DynamicLoadSubclassSingleton() {
        super();
    }
}

/////////////////////////////////////////////////////////////////////////////

package study.design.patterns.creational.singleton.subclassdynamicload.test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import study.design.patterns.creational.singleton.subclassdynamicload.DynamicLoadExtendableSingleton;
import study.design.patterns.creational.singleton.subclassdynamicload.DynamicLoadSubclassSingleton;

public class DynamicLoadExtendableSingletonTest {

    @Test
    public void testIllegalCreation() {
        // The following lines will not compile because the constructor is not visible.
        // DynamicLoadExtendableSingleton instance = new DynamicLoadExtendableSingleton();      
        // DynamicLoadSubclassSingleton instance2 = new DynamicLoadSubclassSingleton();
    }

    @Test
    public void testCreateDynamicLoadExtendableSingleton() {
        System.setProperty("study.design.patterns.creational.singleton.classname", DynamicLoadExtendableSingleton.class.getName());
        DynamicLoadExtendableSingleton singleton = DynamicLoadExtendableSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is an ExtendableSingleton, but not a FixedSubclassSingleton. 
        assertTrue(singleton instanceof DynamicLoadExtendableSingleton);
        assertFalse(singleton instanceof DynamicLoadSubclassSingleton);     
    }

    @Test
    public void testCreateDynamicLoadSubclassSingleton() {
        System.setProperty("study.design.patterns.creational.singleton.classname", DynamicLoadSubclassSingleton.class.getName());
        DynamicLoadExtendableSingleton singleton = DynamicLoadExtendableSingleton.getInstance();
        assertNotNull(singleton);
        // Check that the singleton is a DynamicLoadSubclassSingleton. 
        assertTrue(singleton instanceof DynamicLoadSubclassSingleton);
    }

    @AfterEach
    protected void tearDown() {
        DynamicLoadExtendableSingleton.clearInstance();
    }   
}

/////////////////////////////////////////////////////////////////////////////

Pros: Subclasses do not require a method to activate the class. The only code required is a "thisSingleton" field. The getInstance method does not require modification for each new subclass.

Cons: Reflection can be slower, but since it's only used in one place and only when the singleton is assigned, the risk is minimal. It's possible to get an error if the class name is incorrect. Again, this is a minimal risk.

In summary, while subclassing a singleton may not be common, it is addressed in the GOF book as feasible. There are a few ways to support subclassing singletons, each with benefits and drawbacks. Some of the methods listed above come directly from the book. The method of using reflection was my addition.

like image 156
HeroSteve Avatar answered Oct 06 '22 04:10

HeroSteve


If you can inherit it, it's not really a singleton, since each inherited class will have at least one instance.

However, you can just make the constructor protected.

like image 39
SLaks Avatar answered Oct 06 '22 04:10

SLaks