0

Singleton is a service that require injection of authentication and configuration data. I end with class:

class SingleService {
    private String conn;
    private String user;
    private String pass;

    private SingleService() {
        // Can throw exception!!
        conn = Config.getProperty("conn");
        user = Config.getProperty("user");
        pass = Config.getProperty("pass");
        // Can throw exception!!
        internalService = tryConnect(conn, user, pass);
    }

    private static SingleService instance;

    public static void init() {
        instance = new SingleService();
    }

    public static synchronized SingleService getInstance() {
        if (instance == null) init();
        return instance;
    }
}

Dedicated init() method used for exception handling during application startup to early detect initialization errors early because later we just call getInstance() and doesn't expect to get errors:

class App {
    public static void main(String args[]) {
        try {
             Config.init("classpath:auth.properties");
             SingleService.init();
        } catch (Exception ex) {
            logger.error("Can't init SingleService...");
            System.exit()
        }
        doJob();
    }
    private static void doJob() {
        SingleService.getInstance().doJob();
    }
}

I worry about init() method and singleton class signature. Fill that class was designed badly but don't understand what's wrong.

Is it possible to move away initialization from getSingleton() and synchronized and preserving control on exception during initialization?

2
  • 2
    (1) Singleton is an anti-pattern, and it is usually causing more pain than benefit. (2) Your last sentence (which seems to be the question) It is good move away initialisation from getSingleton and synchronized but I don't figure how... is unclear Commented Jan 29, 2015 at 10:47
  • I rework question for suggestions. Commented Jan 29, 2015 at 10:50

4 Answers 4

3

This is how I would code it so you can throw exceptions if needed but still have a thread safe singleton.

enum SingleService {
    INSTANCE;

    private String conn;
    private String user;
    private String pass;
    private SingleService instance;

    public synchronized void init(Config config) throws SomeException {
        // don't leave in a half state if we fail.
        internalService = null; 

        conn = config.getProperty("conn");
        user = config.getProperty("user");
        pass = config.getProperty("pass");
        internalService = tryConnect(conn, user, pass);
    }

    public synchronized void methodForService() {
        if (internalService == null) throw new IllegalSateException();
        // do work.
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2
SingleService ss1 = SingleService.getInstance();
SingleService.init();
SingleService ss2 = SingleService.getInstance();

So ss1 is a different object than ss2 which is not what Singleton is designed for. If ss1 is modified at anytime ss2 will remain unaffected.

3 Comments

Ok, now I become understand what wrong with init() method, thanks!
Still interesting how to detect initialisation issue on application startup. Seems that fake getInstance() in main() solve issue. I must delete init() or make it private.
Maybe you could make the method getInstance to return null if any problem arise while initialization.
1

Fist of all you souhld not expose object creation method. If you want to check something, than go with asserts or any operation that will not corrupt instance object.

public static void checkIfValid() {
   assert Config.getProperty("conn");// do not corrupt instance object
   assert Config.getProperty("user");
   assert Config.getProperty("pass");
}

public static synchronized SingleService getInstance() {
    if (instance == null){ // only here you can initiate instance object
       instance = new SingleService();
    }
    return instance;
}

Comments

1

My production code for problem I have sought:

public abstract class AbstractCaller<Port> {

    abstract protected Port createPort();

    protected init() {
        Port port = createPort();
        // heavy use of introspection/reflection on **port** object.
        // Results are used later by **call** method.
    }

    public call() {
        // Reflection based on data collected by **init** method.
    }
}

public class ConcreteCaller extends AbstractCaller<ConcretePort> {

    private ConcreteService service = new ConcreteService();

    @Override
    protected ConcretePort createPort() {
        return service.getPort();
    }

    private static class Holder {
        public static ConcreteCaller INSTANCE;
        static {
            INSTANCE = new ConcreteCaller();
            INSTANCE.init();
        }
    }

    public static Caller getInstance() {
        return Holder.INSTANCE;
    }
}

Abstract class has common init method that can only operate on fully initialized concrete class. Inner static class is used for lazy instantiation and perform init invocation.

There is no way to apply init method from superclass constructor to avoid need to call init in each implementation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.