Singleton
Single, as its name this pattern talks about one global references of a class during a process life cycle.Table Of Content
Overview
For applying this pattern, the target class should hide(make it private) its constructor, so no any other object will create one. Then a static instance of the class is initialized by itself and make it accessible by a static method. It's really easy to make a class single. Making the constructor private means no one will be able to make a(separated) instance, and simply the class itself creates one instance of itself and keeps it by a static field which is accessible by a public static method.
package wasys911992.blogger.java.pattern.singleton;
/**
* by https://github.com/911992
* arashmd.blogspot.com
*/
public class SingleGuy{
//making the constructor private
private SingleGuy(){
System.out.print("Hello I'm a Single guy, Forever Alone!\n");
}
//create ONE instance of the class
private static SingleGuy instance=new SingleGuy();
//make the instance accessible for public
public static SingleGuy getInstance(){return SingleGuy.instance;}
}
Usage
The question is "When do we need to use this pattern?" I have read some article about this, some devs say "Always use singleton whenever you need to limit instances from a class to one". While this definition is not wrong, but it doesn't mean we have to use singleton always(just like OOP is not really a wow thing!). For instance class Math would be singleton, it's true, but it's non sense.I think we would have better definition Use Singleton pattern when there is one instance of one
STATEFULL
class is required.
Yes I repeat when ONE STATEFULL object is required.This is nonsense if Math class goes single!(why?) Because each method is completely independent of current object state(stateless). Suppose there is a class that provides methods for encryption/decryption, each method gets an array and returns an array, so because there is no any dependency(no such a shared field[state]) between methods and object, then using singleton approach is nonsense here(but possible). Please considering following samples.
Example (Don't)
Consider we have a Mathematical class, and we need to ensure only one instance of this class is allowed(required), then because this class isStateless
, then singleton pattern is not good idea here.
Bad Approach
Singleton pattern is not perfect for Stateless classes.
Or generally why thinking so much OO when something is completely functional?!
Singleton pattern is not perfect for Stateless classes.
Or generally why thinking so much OO when something is completely functional?!
//not well-designed
class MyMath{
//singleton guys usually have a state
//or some fields shared belong to all threads
private MyMath(){}
//Please do not host functional things in OO way
private static MyMath instance=new MyMath();
public static MyMath getInstance(){return MyMath.instance;}
private final long version=1990L;
public long getVersion(){return this.version;}
public double rectangleArea(double w,double h){
return w*h;
}
public double diamondArea(double w,double h){
return (w*h)/2;
}
}
Good Approach
Use static members for Stateless classes instead of singleton pattern.
Because class MyMath doesn't have any state, so methods are not required to get invoked by an instance and this is a good reason to prefer static members over singleton, in fact it makes our design better.
Use static members for Stateless classes instead of singleton pattern.
class MyMath{
//Good approach, there is no need for singleton pattern because there is no state!
//Generally becasue math stuufs are functional, not thinkink OO gives much sense
private MyMath(){}
public static final long version=1990L;
public static double rectangleArea(double w,double h){
return w*h;
}
public static double diamondArea(double w,double h){
return (w*h)/2;
}
}
Initializing Modes
There are some solutions here for initializing a singleton instance, so does it important? and how should we initialize it?Eager Mode
Initialize the instance with its declaration, use this mode when singleton load is not to huge or you are sure the instance will be used later. as you see, this singleton guy creates initialize the instance reference by its declaration. I would say this is very simple and effective solution, even if you don't need the guy, JRE will not initialize(cannot promise) it of course when there is no dependency.
package wasys911992.blogger.java.pattern.singleton.initmode.eager;
/**
* by https://github.com/911992
* arashmd.blogspot.com
*/
public class EagerOne {
private static EagerOne instance=new EagerOne();
public static EagerOne getInstance(){return EagerOne.instance;}
private EagerOne(){
System.out.println("Horray!, Eager One initialized");
}
}
//============================
public class UsingEager {
public static void main(String[] args) {
System.out.println("I'm going to use my eager singleton guy...");
//JRE will not initialize the static members unless the very first class dependency
EagerOne eo=EagerOne.getInstance();
//using the guy
//....
}
}
Note: In Java, JRE will not initialize the singleton instance till the very first class dependency(hotspot looks like this, not sure about the other VMs).But you could initialize the single instance just before the usage (for example at loading module process) via a
static
block.Lazy Mode
This initialize mode is used when there is no need to use the class for each application start. The instance is initialized by the very first use (not class dependency).As you see in above diagram, the instance is
null
by default, and this is getInstance()
duty to initialize it.
This is not a very hard work, but it makes the code a little curly. The instance will not be initialized till the first getInstance()
method call.
package wasys911992.blogger.java.pattern.singleton.initmode.lazy;
/**
* by https://github.com/911992
* arashmd.blogspot.com
*/
public class LazyOne {
private static volatile LazyOne instance=null;
public synchronized static LazyOne getInstance(){//or it could be in double-check approach for race conditions sake
//it checks the instance nullify for every call, sounds not so cool
if(LazyOne.instance==null){
LazyOne.instance=new LazyOne();
}
return LazyOne.instance;
}
private LazyOne(){
System.out.println("Hi From Lazy One, oh I need some coffee!");
}
}
//=============================
public class UsingLazy {
public static void main(String[] args) {
System.out.println("We are going to use a lazy guy...");
LazyOne lo=LazyOne.getInstance();
//using the guy
//...
}
}
As above code says, the instance is null
by default, but it gets initialized by the very first getInstance()
method call. the synchronized
block is used for avoiding with-same-time initializing which is would appeared in multi-thread application(or the same approach for double null check).The only lack here would be the first if block which checks the nullify of the instance every time, while this check just returns false one time, so let me introduce my customized lazy-init Singleton .
Lazy Mode By Null Object(My Kind of Lazy Mode)
Well there is no surprise, the only difference is that there is no nullify check every time. We would redirect a work as easy as ABC with multiple implementation by interfaces and abstract classes.So I combined the
Singleton
with Null Object
pattern. At the first the instance is initialized as a null object, not null
.A fake(null/light) instance is initialized by declaration(say eager), and just for the first request, the fake business initializes(creates) the actual business and replaces the fake business implementation with actual one. So here we are not going to check the instance null state every time, instead swap the implemntation of the businesses. First we extract all business methods(what actually need to be done) from the singleton class to an interface or abstract class named
Business
. Next two classes Null Business and Actual Business implement the business interface that the null one is initialized by singleton business field declaration that just responsible to create the actual one in order to redirect all next requests to it.There will be only one nullify checking here by null one, and singleton is ensured that business instance points permanently to the actual implementation any time.
package wasys911992.blogger.java.pattern.singleton.initmode.lazy.nullobj;
/**
* by https://github.com/911992
* arashmd.blogspot.com
*/
interface Business {
void doSomething();
Object getContext();
}
//=============================
class SingleGuy {
private static SingleGuy instance=new SingleGuy();
public static SingleGuy getInstance(){
return SingleGuy.instance;
}
//by default business is null one
private Business buss=new NullBusiness();
//a bridge to set the actual business
void setActualBusiness(Business buss){
this.buss=buss;
}
private SingleGuy(){
System.out.println("Singleton initilized!");
}
public void doWork(){
//doing the work...
buss.doSomething();
}
public Object getContext(){
return buss.getContext();
}
}
//==============================
class ActualBusiness implements Business {
@Override
public void doSomething(){
//do some business
System.out.println("Doing something nonesens...!");
}
@Override
public Object getContext() {
return new secret_context();
}
}
//===============================
class NullBusiness implements Business {
@Override
public void doSomething(){
initActualBusiness();
//forward the request to the actual business
realBusiness.doSomething();
}
@Override
public String getContext() {
initActualBusiness();
//get and return the context from the actual business
return realBusiness.getContext();
}private Business realBusiness=null;
private synchronized void initActualBusiness(){
if(realBusiness!=null){return; }
System.out.println("initilizing real business....");
//Instantiate the actual business
realBusiness=new ActualBusiness();
//set the actual one with null one for fortune calls
SingleGuy.getInstance().setActualBusiness(realBusiness);
}
}
Examples
Simple Context Manager(Naming and Directory Interface, NDI)
Consider a simple central resource/context manager(NDI), clients can add, remove, or update resources. Each resource has one name and could be in anything(data-type). Also clients would listen for any(particular) resource manipulation event. Okay, as you see in above example theResourceManager
class follows singleton rules, constructor is hidden, a pointer(instance) from itself(eager mode) which is made accessible by a static method.The
Listener
interface is used for communicating between listeners and the manager, in fact manager informs the client listeners by this interface.Class
ListenerHolder
, it holds listener which are belong to a certain resource, in fact each resource would have many listeners.
The ResourceManager
class has a map(list) of this class, because there are many resources belong to resource manager.And finally
ClientA
, and ChangeListener
classes, these two guys are just for testing the system and showing how does outsiders would communicate with resource manager.And the code
package wasys911992.blogger.java.pattern.singleton.example0;
/**
* by https://github.com/911992
* arashmd.blogspot.com
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Scanner;
public class ResourceManager {
private static ResourceManager instance=new ResourceManager();
public static ResourceManager getInstance(){return ResourceManager.instance;}
private ResourceManager(){
resources=new HashMap<String,Object>(5);
listeners=new Hashtable<String,ListenerHolder>(5);
//adding some default value
resources.put("name", "Resource Manager Singleton");
resources.put("version", 1);
resources.put("friends", "Sahand ,Pedram ,Aidin ,Danial");
System.out.println("Resource Manager Initilized");
}
private HashMap<String, Object> resources;
private Hashtable<String, ListenerHolder> listeners;
public void addListener(String name,Listener l){
synchronized (resources) {
if(resources.containsKey(name)){
if(listeners.containsKey(name)){
listeners.get(name).addListener(l);
}else{
ListenerHolder lh=new ListenerHolder(l);
listeners.put(name, lh);
}
}
}
}
public Object getObject(String name){
return resources.get(name);
}
public void updateResource(String name,Object arg){
synchronized (resources) {
if(resources.containsKey(name)){
resources.remove(name);
resources.put(name, arg);
}
}if(!listeners.containsKey(name)){return;}
for(Listener lx : listeners.get(name).getList()){
lx.onUpdate(name,arg);
}
}
public void addResource(String name,Object arg){
synchronized (resources) {
resources.put(name, arg);
}
}
}
//==================================
public interface Listener{
public void onUpdate(String name,Object arg);
}
//==================================
class ListenerHolder{
public ListenerHolder(Listener l){this.listeners.add(l);}
private List<Listener> listeners=new ArrayList<>(5);
public void addListener(Listener l){listeners.add(l);}
List<Listener> getList(){return listeners;}
}
//==================================
//and a simple client
public class ClientA{
private ResourceManager r;
private String sysVersion="2EB";
private Scanner s;
public static void main(String arg[]) {
ClientA a=new ClientA();
a.changeSysVersion();
}
public ClientA(){
s=new Scanner(System.in);
r=ResourceManager.getInstance();
r.addResource("sysVersion", sysVersion);
Listener l=new ChangeListener();
//add l listener for "sysVersion" resource changes
r.addListener("sysVersion", l);
String f=(String)r.getObject("friends");
System.out.println("Friends: "+f);
}
void changeSysVersion(){
System.out.print("Enter new value for \"sysVersion\"(current value= "+sysVersion+"): ");
String newVal=s.nextLine();
r.updateResource("sysVersion",newVal);
}
}
//==================================
//simple change listener
class ChangeListener implements Listener{
@Override
public void onUpdate(String name,Object arg) {
System.out.println("Change Listener: updated resource("+name+") value = "+arg);
}
}
Okay, in above code, check the ClientA
constructor, it gets ResourceManager
instance, and add a resource name sysVersion
, then adds a ChangeListener
instance as resource listener for sysVersion
resource, and change the value implicitly that cause resource manager triggers the related listeners.
Simple Transaction Manager
This instance is talking about a central transaction manager, this one has a singleton classTransactionMgr
which takes care about transaction with a specific data source(database).
Client gets a transaction id (long long value) which points to a real transaction inside the transaction manager(assume a pointer, if you are a OOP-nism, consider use the object instead of pointer). Because transaction manager is singleton and is accessible every where in workspace, so different modules just need to use shared transaction with just one long value.
The same thing is available in J2EE, context manager, and Faces Context in jsf. As you see in above diagram,
TransactionMgr
is a singleton guy, so it means the whole application would simply uses transactions in a central-ready module.
And the code, there is no need for comments. Members tell everything you need dude.
package wasys911992.blogger.java.pattern.singleton.example.tranmgr;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Properties;
/**
* by https://github.com/911992
* arashmd.blogspot.com
*/
public class TransactionMgr {
private static TransactionMgr instance=new TransactionMgr();
private java.sql.Driver driver=null;
private java.util.HashMap<Long, java.sql.Connection> batches=new HashMap<Long,java.sql.Connection>();
private final int maxTranInstance=16;
private int runningTransaction=0;
private long lastTranId=0;
private final String connectionString="jdbc:postgresql://127.0.0.1:17001/_ds";
private final Properties connectionPeroperties=new Properties();
private final String driverClassName="org.postgresql.Driver";
private Object waitLock=new Object();
private TransactionMgr(){
System.out.println("Connection Manager is initializing\nusing "+this.driverClassName+" as default driver...");
try {
this.driver=(java.sql.Driver)Class.forName(driverClassName).newInstance();
//setting default connection properties
this.connectionPeroperties.put("user", "__arash_");
this.connectionPeroperties.put("password", "one simple password!!!");
System.out.println("Connection Manager has initialized successfully, ready for action!");
} catch (Exception e) {
System.err.println("Error! could not load the driver! "+e.getMessage());
e.printStackTrace();
}
}
public static TransactionMgr getInstance(){return TransactionMgr.instance;}
public synchronized void setDriver(String dName) throws Exception{
this.driver=(java.sql.Driver)Class.forName(dName).newInstance();
}
public java.sql.Connection getANativeConnection() throws SQLException{
return driver.connect(connectionString, connectionPeroperties);
}
public long beginTransaction() throws SQLException{
synchronized (waitLock) {
if(runningTransaction==maxTranInstance){
try {waitLock.wait();} catch(InterruptedException e){e.printStackTrace();}
}
Connection c=driver.connect(connectionString, connectionPeroperties);
c.setAutoCommit(false);
batches.put(++lastTranId, c);
runningTransaction++;
return lastTranId;
}
}
public int addToTransation(long tranId,String command) throws SQLException{
if(!batches.containsKey(tranId)){return -2;}
return batches.get(tranId).createStatement().executeUpdate(command);
}
public void commitTransaction(long tranId) throws SQLException{
if(!batches.containsKey(tranId)){return;}
batches.get(tranId).commit();
removeTransaction(tranId);
}
public void rollbackTransaction(long tranId){
if(!batches.containsKey(tranId)){return;}
removeTransaction(tranId);
}
private void removeTransaction(long tranId){
synchronized (waitLock) {
if(!batches.containsKey(tranId)){return;}
runningTransaction--;
try {batches.remove(tranId).close();} catch (SQLException e) {e.printStackTrace();}
try{waitLock.notify();}catch(Exception e){}
}
}
}
//=====================================
public class UserInput {
private static java.util.Scanner reader=null;
static{
reader=new java.util.Scanner(System.in);
}
public static void main(String[] args) {
System.out.println("We are going to utilize the singleton");
UserInput.getUserInfo();
}
private static void getUserInfo(){
try{
TransactionMgr cm=TransactionMgr.getInstance();
//Connection c=cm.getANativeConnection();
//c.createStatement().execute("create table \"user\"(\"name\" character varying(256))");
long l=cm.beginTransaction();
System.out.println("Transaction had began, please enter names you want to insert");
System.out.println("you may input :q for rollbacking transaction, or :p for commit...");
String name;
while(true){
name=reader.nextLine();
if(name.equals(":q")){cm.rollbackTransaction(l);
System.out.println("Transaction has rollbacked successfully....");break;}
if(name.equals(":p")){cm.commitTransaction(l);
System.out.println("Transaction has Commited successfully....");break;}
cm.addToTransation(l, "insert into \"user\" values('"+name+"')");
}
System.out.println("Thanks for using connection manager!");
}catch(Exception e){e.printStackTrace();}
}
}
Note: Prepare external dependencies(database, driver) before you test it :).Note: I have not tested the codes, so consider get gifted by some
Exceptions
by dear JVM.