20 December 2012

Singleton Design Pattern

In Part 1 of these articles we discussed the negative aspects of the Singleton Design Pattern. Now let us discuss what can be done to reduce the negative aspects. The first item that springs to mind is that we can loosen the coupling by returning an interface rather than the object itself. Keep in mind that by doing this it will no longer be a Singleton when considering a strict definition.

RuleSystem.java
package singletonloosed;

public class RuleSystem implements IRuleSystem {

  private static volatile IRuleSystem ruleSystem;

  public RuleSystem() {
    
  }  
  
  private static synchronized void initializeInstance() {
    ruleSystem = new RuleSystem();
  }

  public static IRuleSystem getInstance() {
    if (ruleSystem == null) {
      initializeInstance();
    }
    return ruleSystem;
  }
}
IRuleSystem.java
package singletonloosed;

public interface IRuleSystem {
  
}

We now can change the object type being returned by the “Singleton” as long as it implements the IRuleSystem interface. We could create FantasyRule and SuperHeroRule classes that implement the IRuleSystem interface and return either one. We can add or remove a variety of rule systems to the RuleSystem class without changing code where the class is called. We can change the code in one location, which is the goal of being loosely coupled.

This design needs more thought. How will our RuleSystem class know which rule system to return? The option I am going with here is to pass in a parameter to the getInstance method. Let us write some code starting with our “Singleton” class.

RuleSystems.java
package singletonimproved;

import java.util.*;
import singletonimproved.RuleSystemEnum;

public class RuleSystems {

  private static volatile HashMap<RuleSystemEnum, IRuleSystem> ruleSystems;

  private RuleSystems() {
  }

  private static synchronized void initializeInstance() {
    ruleSystems = new HashMap(2);
    ruleSystems.put(RuleSystemEnum.SUPER, new SuperRuleSystem());
    ruleSystems.put(RuleSystemEnum.FANTASY, new FantasyRuleSystem());
  }

  public static IRuleSystem getInstance(RuleSystemEnum system) {
    IRuleSystem returnValue = null;
    if (ruleSystems == null) {
      initializeInstance();
    }

    returnValue = ruleSystems.get(system);

    return returnValue;
  }
}

Wow! Our Singleton Design pattern has been transformed into a Factory Design pattern. Does this mean, when we are tempted to use a Singleton, we should instead use a Factory instead? I would say the answer is yes 99% of the time. Now for our Enumerations which will help keep our code uncluttered.

RuleSystemEnum.java
package singletonimproved;

public enum RuleSystemEnum {
  FANTASY,
  SUPER
}

Next our simple interface.

IRuleSystem.java
package singletonimproved;

public interface IRuleSystem {
  
}

Followed by two rule systems which implement our interface.

FantasyRuleSystem.java
package singletonimproved;

public class FantasyRuleSystem implements IRuleSystem {
  
  public String toString() {
    return "I am a Medieval Fantasy rule system!";
  }
  
}
SuperRuleSystem.java
package singletonimproved;

public class SuperRuleSystem implements IRuleSystem {
  
  public String toString() {
    return "I am a system for Super Heros!";
  }
  
}

Now lets add a class that will use our Factory and show us how it works.

SuperRuleSystem.java
package singletonimproved;

public class SingletonImproved {

  public static void main(String[] args) {
    RuleSystems rules = new RuleSystems();
    IRuleSystem rule = RuleSystems.getInstance(RuleSystemEnum.SUPER);
    System.out.println(rule.toString());
    rule = RuleSystems.getInstance(RuleSystemEnum.FANTASY);
    System.out.println(rule.toString());
  }
}

When run you should see results similar to the following.

SingletonImproved Results
I am a system for Super Heros!
I am a Medieval Fantasy rule system!

What hasn’t been addressed is the global nature of using Singleton’s and their affect on testing/debugging. I will leave those subjects to others for now as I am anxious to move onto another design pattern.


Less Is More ~ Older posts are available in the archive.