使用init方法的Java单例

时间:2015-01-29 10:44:34

标签: java design-patterns initialization singleton

Singleton是一种需要注入身份验证和配置数据的服务。我以课程结束:

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;
    }
}

专用init()方法,用于在应用程序启动期间进行异常处理,以便尽早检测初始化错误,因为稍后我们只需调用getInstance()并且不会出现错误:

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();
    }
}

我担心init()方法和单例类签名。填写该课程的设计很糟糕,但不明白错误。

是否可以从getSingleton()synchronized移除初始化并在初始化期间保留对异常的控制?

5 个答案:

答案 0 :(得分:3)

这就是我编写它的方式,这样你就可以在需要时抛出异常,但仍然有一个线程安全的单例。

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.
    }
}

答案 1 :(得分:2)

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

所以ss1是一个与ss2不同的对象,而不是Singleton的设计目标。如果ss1随时被ss2修改,{{1}}将不受影响。

答案 2 :(得分:1)

所有你不应该暴露对象创建方法的拳头。如果要检查某些内容,请使用断言或任何不会损坏实例对象的操作。

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;
}

答案 3 :(得分:0)

init()方法应该是私有的......

答案 4 :(得分:0)

我寻求解决的生产代码:

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;
    }
}

抽象类具有通用的init方法,该方法只能对完全初始化的具体类进行操作。内部静态类用于延迟实例化并执行init调用。

无法通过超类构造函数应用init方法来避免在每个实现中都需要调用init