instanciação única com argumentos
-
05-07-2019 - |
Pergunta
Eu tenho uma classe (RInterfaceHL) que chama outra classe (JRIEngine) que fornece métodos nativos em um aplicativo single-threaded. Por isso eu só quero ter uma única instância da minha classe (RInterfaceHL) por JVM.
Eu posso usar o padrão singleton com inicialização estática para garantir que apenas um único instanciação de RInterfaceHL, mas RInterfaceHL precisa construir uma instância de JRIEngine e fornecê-lo com um parâmetro de auto-retorno. Como posso fornecer, de uma forma thread-safe, uma única instância de RInterfaceHL que aceita um parâmetro de auto-retorno para a construção de um único JRIEngine? Estou usando JDK6.
NB:. Este nome semelhante questão não respondeu à minha pergunta ??p >
Solução
A modificação do padrão Singleton que usos Bill Pugh no suporte demanda idioma . Este é o segmento de seguros sem a sobrecarga de construções de linguagem especializadas (ou seja volátil ou sincronizado):
public final class RInterfaceHL {
/**
* Private constructor prevents instantiation from other classes.
*/
private RInterfaceHL() { }
/**
* R REPL (read-evaluate-parse loop) handler.
*/
private static RMainLoopCallbacks rloopHandler = null;
/**
* SingletonHolder is loaded, and the static initializer executed,
* on the first execution of Singleton.getInstance() or the first
* access to SingletonHolder.INSTANCE, not before.
*/
private static final class SingletonHolder {
/**
* Singleton instance, with static initializer.
*/
private static final RInterfaceHL INSTANCE = initRInterfaceHL();
/**
* Initialize RInterfaceHL singleton instance using rLoopHandler from
* outer class.
*
* @return RInterfaceHL instance
*/
private static RInterfaceHL initRInterfaceHL() {
try {
return new RInterfaceHL(rloopHandler);
} catch (REngineException e) {
// a static initializer cannot throw exceptions
// but it can throw an ExceptionInInitializerError
throw new ExceptionInInitializerError(e);
}
}
/**
* Prevent instantiation.
*/
private SingletonHolder() {
}
/**
* Get singleton RInterfaceHL.
*
* @return RInterfaceHL singleton.
*/
public static RInterfaceHL getInstance() {
return SingletonHolder.INSTANCE;
}
}
/**
* Return the singleton instance of RInterfaceHL. Only the first call to
* this will establish the rloopHandler.
*
* @param rloopHandler
* R REPL handler supplied by client.
* @return RInterfaceHL singleton instance
* @throws REngineException
* if REngine cannot be created
*/
public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
throws REngineException {
RInterfaceHL.rloopHandler = rloopHandler;
RInterfaceHL instance = null;
try {
instance = SingletonHolder.getInstance();
} catch (ExceptionInInitializerError e) {
// rethrow exception that occurred in the initializer
// so our caller can deal with it
Throwable exceptionInInit = e.getCause();
throw new REngineException(null, exceptionInInit.getMessage());
}
return instance;
}
/**
* org.rosuda.REngine.REngine high level R interface.
*/
private REngine rosudaEngine = null;
/**
* Construct new RInterfaceHL. Only ever gets called once by
* {@link SingletonHolder.initRInterfaceHL}.
*
* @param rloopHandler
* R REPL handler supplied by client.
* @throws REngineException
* if R cannot be loaded.
*/
private RInterfaceHL(RMainLoopCallbacks rloopHandler)
throws REngineException {
// tell Rengine code not to die if it can't
// load the JRI native DLLs. This allows
// us to catch the UnsatisfiedLinkError
// ourselves
System.setProperty("jri.ignore.ule", "yes");
rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
}
}
Outras dicas
public class RInterfaceHL {
private static RInterfaceHL theInstance;
private final JRIEngine engine;
private RInterfaceHL(JRIEngine engine) {
this.engine = engine;
}
public static synchronized RInterfaceHL getInstance() {
if (theInstance == null) {
throw new IllegalStateException("not initialized");
}
return theInstance;
}
public static synchronized void initialize(String loopback) {
if (theInstance != null) {
throw new IllegalStateException("already initialized");
}
theInstance = new RInterfaceHL(new JRIEngine(loopback));
}
...
}
EDIT: Devo acrescentar que, se você está construindo coisas para execução em um servlet ou similar recipiente, usando o padrão Singleton puro é provavelmente uma má idéia. Um dos mecanismos IoC injeção / dependência é uma idéia melhor; por exemplo. Primavera como sugerido em outra resposta. Isso permite que você alcance seus "singletons" para o recipiente.
Pode ser um exagero se você está apenas fazendo uma pequena aplicação, mas um quadro de injeção de dependência como Spring Framework pode dar-lhe Singleton comportamento sem a necessidade de construir manualmente e inicializar o objeto estático com a mão.
A injeção de dependência "container" vai construir e fio que você Singleton e suas classes de dependência juntos, e pode ser configurado para fazer o seu objeto uma instância singleton dentro do recipiente.
Há um pouco de uma curva de aprendizado, se você não tiver usado Primavera antes, mas é um quadro muito popular, e provavelmente irá atendê-lo bem.