Singleton Nested Class Holder idiom works by on-demand initialization of singleton objects during class loading. This gives the impression that it does not work when the object initialization depends on another resource for initialization. Here, we show that is not the case with an interesting example.
For example, here is a stream reader class to initialize a singleton stream reader object that could then be used to parse text contained in a URL. The URL to be parsed is input at run-time by the user.
/*attempt to make reader a singleton */
public class Reader {
private DataBean data;
private InputStream ir;
private Reader (DataBean d) {
data = d;
try {
ir = new URL(data.getResource()).openStream();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
********\ Oh oh, Databean not available yet **********/
private static class ReaderHolder {
private static final Reader reader = new Reader();
}
public Reader getInstance (Databean d) {
return ReaderHolder.reader;
}
}
Now, the problem here seems to result from the fact that the Databean object is not available yet as it will only be made available later by the user.
We work around this with a Context object that encapsulates the Databean object and may be either be passed around easily or making Context itself a singleton with early-initialization.
/*Context class*/
public class Context {
public static final Context INSTANCE = new Context();
private DataBean dataBean;
private Context() {
}
/**
* @return the db
*/
public DataBean getDataBean() {
return dataBean;
}
/**
* @param db the db to set
*/
public void setDataBean(DataBean db) {
dataBean = db;
}
}
Now, our reader is modified to look like this:
/*Reader is a singleton */
public class Reader {
private InputStreamReader ir;
private static class ReaderHolder {
private static final Reader reader = new Reader();
}
private Reader () {
try {
InputStream stream = new URL(Context.INSTANCE.getDataBean().getResource()).openStream();
ir = new InputStreamReader(stream);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @return the ir
*/
public InputStreamReader getIr() {
return ir;
}
public Reader getInstance () {
return ReaderHolder.reader;
}
}
For example, here is a stream reader class to initialize a singleton stream reader object that could then be used to parse text contained in a URL. The URL to be parsed is input at run-time by the user.
/*attempt to make reader a singleton */
public class Reader {
private DataBean data;
private InputStream ir;
private Reader (DataBean d) {
data = d;
try {
ir = new URL(data.getResource()).openStream();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
********\ Oh oh, Databean not available yet **********/
private static class ReaderHolder {
private static final Reader reader = new Reader();
}
public Reader getInstance (Databean d) {
return ReaderHolder.reader;
}
}
Now, the problem here seems to result from the fact that the Databean object is not available yet as it will only be made available later by the user.
We work around this with a Context object that encapsulates the Databean object and may be either be passed around easily or making Context itself a singleton with early-initialization.
/*Context class*/
public class Context {
public static final Context INSTANCE = new Context();
private DataBean dataBean;
private Context() {
}
/**
* @return the db
*/
public DataBean getDataBean() {
return dataBean;
}
/**
* @param db the db to set
*/
public void setDataBean(DataBean db) {
dataBean = db;
}
}
Now, our reader is modified to look like this:
/*Reader is a singleton */
public class Reader {
private InputStreamReader ir;
private static class ReaderHolder {
private static final Reader reader = new Reader();
}
private Reader () {
try {
InputStream stream = new URL(Context.INSTANCE.getDataBean().getResource()).openStream();
ir = new InputStreamReader(stream);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @return the ir
*/
public InputStreamReader getIr() {
return ir;
}
public Reader getInstance () {
return ReaderHolder.reader;
}
}
This is actually an interesting enhancement opportunity to plain Java programs derived from J2EE and Spring framework.
No comments:
Post a Comment