A parameter is assigned to the constructor of the singleton class type (as we did in step two). We initialize a public static getObject or getInstance function with a class object as the return value.
In Java, Singleton is a design pattern that ensures that a class can only have one object. To create a singleton class, a class must implement the following properties: Create a private constructor of the class to restrict object creation outside of the class.
There are two forms of singleton design pattern, which are: Early Instantiation: The object creation takes place at the load time. Lazy Instantiation: The object creation is done according to the requirement.
I'll make my point very clear: a singleton with parameters is not a singleton.
A singleton, by definition, is an object you want to be instantiated no more than once. If you are trying to feed parameters to the constructor, what is the point of the singleton?
You have two options. If you want your singleton to be initialized with some data, you may load it with data after instantiation, like so:
SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data
If the operation your singleton is performing is recurring, and with different parameters every time, you might as well pass the parameters to the main method being executed:
SingletonObj singleton = SingletonObj.getInstance();
singleton.doSomething(paramA, paramB); // pass parameters on execution
In any case, instantiation will always be parameter-less. Otherwise your singleton is not a singleton.
I think you need something like a factory to have objects with various parameters instantiated and reused. It could be implemented by using a synchronized HashMap
or ConcurrentHashMap
map a parameter (an Integer
for an example) to your 'singleton' parameterizable class.
Although you might get to the point where you should use regular, non-singleton classes instead (for example needing 10.000 differently parametrized singleton).
Here is an example for such store:
public final class UsefulObjFactory {
private static Map<Integer, UsefulObj> store =
new HashMap<Integer, UsefulObj>();
public static final class UsefulObj {
private UsefulObj(int parameter) {
// init
}
public void someUsefulMethod() {
// some useful operation
}
}
public static UsefulObj get(int parameter) {
synchronized (store) {
UsefulObj result = store.get(parameter);
if (result == null) {
result = new UsefulObj(parameter);
store.put(parameter, result);
}
return result;
}
}
}
To push it even further, the Java enum
s can be also considered (or used as) parametrized singletons, although allowing only a fixed number static variants.
However, if you need a distributed1 solution, consider some lateral caching solution. For example: EHCache, Terracotta, etc.
1 in the sense of spanning multiple VMs on probably multiple computers.
You can add a configurable initialization method in order to separate instantiation from getting.
public class Singleton {
private static Singleton singleton = null;
private final int x;
private Singleton(int x) {
this.x = x;
}
public static Singleton getInstance() {
if(singleton == null) {
throw new AssertionError("You have to call init first");
}
return singleton;
}
public synchronized static Singleton init(int x) {
if (singleton != null)
{
// in my opinion this is optional, but for the purists it ensures
// that you only ever get the same instance when you call getInstance
throw new AssertionError("You already initialized me");
}
singleton = new Singleton(x);
return singleton;
}
}
Then you can call Singleton.init(123)
once to configure it, for example in your app startup.
You can also use the Builder pattern if you want to show that some parameters are mandatory.
public enum EnumSingleton {
INSTANCE;
private String name; // Mandatory
private Double age = null; // Not Mandatory
private void build(SingletonBuilder builder) {
this.name = builder.name;
this.age = builder.age;
}
// Static getter
public static EnumSingleton getSingleton() {
return INSTANCE;
}
public void print() {
System.out.println("Name "+name + ", age: "+age);
}
public static class SingletonBuilder {
private final String name; // Mandatory
private Double age = null; // Not Mandatory
private SingletonBuilder(){
name = null;
}
SingletonBuilder(String name) {
this.name = name;
}
public SingletonBuilder age(double age) {
this.age = age;
return this;
}
public void build(){
EnumSingleton.INSTANCE.build(this);
}
}
}
Then you could create/instantiate/parametrized it as follow:
public static void main(String[] args) {
new EnumSingleton.SingletonBuilder("nico").age(41).build();
EnumSingleton.getSingleton().print();
}
Surprised that no one mentioned how a logger is created/retrieved. For example, below shows how Log4J logger is retrieved.
// Retrieve a logger named according to the value of the name parameter. If the named logger already exists, then the existing instance will be returned. Otherwise, a new instance is created.
public static Logger getLogger(String name)
There are some levels of indirections, but the key part is below method which pretty much tells everything about how it works. It uses a hash table to store the exiting loggers and the key is derived from name. If the logger doesn't exist for a give name, it uses a factory to create the logger and then adds it to the hash table.
69 Hashtable ht;
...
258 public
259 Logger getLogger(String name, LoggerFactory factory) {
260 //System.out.println("getInstance("+name+") called.");
261 CategoryKey key = new CategoryKey(name);
262 // Synchronize to prevent write conflicts. Read conflicts (in
263 // getChainedLevel method) are possible only if variable
264 // assignments are non-atomic.
265 Logger logger;
266
267 synchronized(ht) {
268 Object o = ht.get(key);
269 if(o == null) {
270 logger = factory.makeNewLoggerInstance(name);
271 logger.setHierarchy(this);
272 ht.put(key, logger);
273 updateParents(logger);
274 return logger;
275 } else if(o instanceof Logger) {
276 return (Logger) o;
277 }
...
"A singleton with parameters is not a singleton" statement is not completely correct. We need to analyze this from the application perspective rather than from the code perspective.
We build singleton class to create a single instance of an object in one application run. By having a constructor with parameter, you can build flexibility into your code to change some attributes of your singleton object every time you run you application. This is not a violation of Singleton pattern. It looks like a violation if you see this from code perspective.
Design Patterns are there to help us write flexible and extendable code, not to hinder us writing good code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With