O exemplo mais simples de uma fachada com o singleton é o seguinte :
public class FachadaTeste {
private static FachadaTeste fachadaSingleton = null;
// repare no construtor privado, somente métodos dessa classe pode utiliza-lo (no caso getInstance()
private FachadaTeste() {
//algum código aqui
}
// retorna a única instancia
public static FachadaTeste getInstance() {
if (fachadaSingleton == null) {
fachadaSingleton = new FachadaTeste();
}
return fachadaSingleton;
}
//métodos da fachada
public void algumMetodoDaFachada1() {
//algum codigo aqui
}
public void algumMetodoDaFachada2() {
//algum codigo aqui
}
}
Num sistema multithreading seria bom, alias seria necessario, sincronizar a criação do singleton. Então o metodo getInstance ficaria mais um menos assim:
public static synchronized FachadaTeste getInstance() {
if (fachadaSingleton == null) {
fachadaSingleton = new FachadaTeste();
}
return fachadaSingleton;
}
Louds, o “double-checked locking” é um problema famoso… o seu código pode dar pau se o cara compilar com otimização e rodar numa máquina com mais de um processador…
aqui mesmo no GUJ tem uma discussão sobre isso, mas to com preguiça de procurar…
realmente, double checked locking pode falhar se não forem utilizadas barriers em processadores como p4 e xp.
Porem usando os compiladores da IBM e da sun, voce não vai ter problema algum porque eles de fato nada otimizam o bytecode; o jikes so da strip na tabela de linhas, com javac da Sun nunca vi qualquer tipo de otimização, por mais simples que seja ocorrendo.
Agora se voce usar coisas como BLOAT, ai pode dar problema mesmo…
bao, acho que a melhor maneira para se iniciar um singleton eh fazendo um bloco static na classe, dessa maneira o singleton sera iniciado quando a classe for carregada sem a necessidade de se sincronizar um metodo!
static{
fachadaSingleton = new FachadaTeste();
}
public FachadaTeste getInstance() {
return fachadaSingleton;
}
bom a ideia de se colocar a criacao em um bloco static se da para que vc nao precise sincronizar o getInstance.
Vamos supor que o computador onde essa app esta rodando tenha varios processadores, logo se vc sincronizar o acesso vc quebra o multiprocessamento criando sem necessidade uma fila pelo objeto!
Eu entendi o sincronismo que vc criou usando esse monitor, mas acho que não faz muito sentido!
Resumindo a ideia:
-A solucao que eu apresentei possibilita a utilizacao do getInstance em paralelo melhorando a performance em ambientes multiprocessados.
Galera,
Acho que não precisa de código sincronizado, senão vejamos:
[code]class MySingleton {
private static MySingleton me = new MySingleton();
private MySingleton() {}
public static MySingleton getInstance() {return me;}
}
class TestSingleton {
static MySingleton s1 = MySingleton.getInstance();
static MySingleton s2 = MySingleton.getInstance();
public static void main(String[] args) {
System.out.println("So tenho UMA instancia do Singleton? " + (s1 == s2));
}[/code]
A saída produz o resultado desejado: true. Ao mesmo tempo esse código é thread-safe, pois a variável de classe “me” é inicializada durante o carregamento da classe, antes de qualquer thread executá-la. Mas, fica a dúvida: seria possível ter MAIS DE UMA INSTÂNCIA DO MESMO SINGLETON? Bem, se vc usar a classe de forma normal a coisa funciona bem. Mas, é possível sim ter mais de uma instância. Veja como:
import java.net.*;
import java.lang.reflect.*;
public class TestSingleton {
public static void main(String [] args) throws Exception {
URL url = new URL("file:subdir/");
URL[] urls = new URL[]{url};
URLClassLoader loader1 = new URLClassLoader(urls);
URLClassLoader loader2 = new URLClassLoader(urls);
// carregamos a classe na mão...
Class cls1 = loader1.loadClass("MySingleton");
Class cls2 = loader2.loadClass("MySingleton");
// instanciamos agora os dois Singletons por invocacao reflexiva
Method m1 = cls1.getMethod("getInstance", null);
Object o1 = m1.invoke(null, null);
Method m2 = cls2.getMethod("getInstance", null);
Object o2 = m2.invoke(null, null);
//Comprovando que temos dois Singletons diferentes
System.out.println("Singleton 1:" + o1);
System.out.println("Singleton 2:" + o2);
}
}
Saída:
Singleton 1:MySingleton@a18aa2
Singleton 2:MySingleton@194ca6c
Uma vez que temos duas instâncias de Singleton, podemos invocar qualquer um de seus métodos de forma reflexiva. Infelizmente não conheço nenhuma forma de evitar isso. Talvez seja possível evitar isso usando SecurityManager.
Até a próxima…