You are currently browsing the monthly archive for setembro 2009.

Essa série de posts irão discorrer sobre um tema muito discutido na web, algo que gerou certa “polêmica” para quem sempre teve como base o uso de Design Patterns em seus projetos. Seria ou não Singleton um anti-pattern? Bom, para tomarmos qualquer tipo de decisão nesse sentido devemos primeiro nos ater aos conceitos principais: o que é um Singleton e o que é Injeção de Dependência.

A série está dividida em 3 partes, sendo que a parte 1 demonstrará a motivação e a utilização do Singleton, além de suas vantagens e desvantagens na implementação do pattern. Já a parte 2 introduzirá o conceito e aplicações do IoC ou Inversão de Controle, além de frameworks Java que possibilitam sua utilização. Finalmente a parte 3 fará um balanço das duas abordagens, mostrando o que uma tem a ver com a outra, além de algum código para comparação.

Singleton

Esse Design Pattern pertence a categoria chamada “Criacional”, pois cuida da maneira que determinados objetos são criados. Ele é um dos mais simples dentre os padrões propostos pela Gangue dos Quatro, sendo amplamente utilizado, em alguns casos até de maneira abusiva.

Seu propósito é prover um mecanismo para que classes permitam criar apenas UMA ÚNICA instância na aplicação. Esse tipo de abordagem permite a criação de técnicas de cache por exemplo, mas temos que tomar cuidado em seu uso, pois muitas vezes esse Singleton acaba fazendo o papel de um tipo de “variável global”, algo bem estranho do ponto de vista Orientado a Objetos, até porque essa não é a responsabilidade do pattern, definido pela Gangue dos Quatro.

Exemplo do uso de um Singleton

Imaginemos um cenário de uma camada de abstração de um banco de dados (um DAO – Data Access Object, talvez) de uma aplicação qualquer. Teríamos uma classe DAO para cada entidade do sistema (ClienteDAO, ProdutoDAO, PedidoDAO por exemplo) onde teríamos métodos do tipo findById(Integer id), findByExample(DAO example), findAll(), etc.

class ClienteDAO {

public boolean create(Cliente cliente) { }

public Cliente findById(Integer id) { }

public List<Cliente> findAll() { }

}

Até aí tudo bem, mas sabemos que para qualquer tipo de operação realizada no banco de dados, precisamos abrir uma conexão e passá-la para que o comando (INSERT, UPDATE, DELETE, XPTO) possa ser executado no SGBD. Um jeito simples de resolver isso é criar uma classe MySQLConnection, onde temos método do tipo getConnection(), closeConnection(), onde teríamos a abstração da conexão que necessitamos.

import java.sql.*;

class ClienteDAO {

public boolean create(Cliente cliente) {
try {
Connection conn = MySQLConnection.newConnection().getConnection();
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO TB_CLIENTE VALUES (?, ?)");
pstmt.setInt(cliente.getId());
pstmt.setNome(cliente.getNome());
} catch(SQLException ex) {
ex.printStackTrace();
} finnaly {
pstmt.close();
conn.close();
}
}
}

O que podemos fazer para que a instância de conexão seja única para a aplicação inteira? A primeira vista, resolvemos isso transformando a classe MySQLConnection em um Singleton.

import java.sql.*;

public class MySQLConnection {

private static MySQLConnection conn;
private Connection sqlConn;

private MySQLConnection() {

try {
Class.forName("com.mysql.jdbc.Driver");
sqlConn = DriverManager.getConnection("url", "user", "pass");
} catch(ClassNotFoundException ex) {
ex.printStackTrace();
}
}

public static MySQLConnection newConnection() {
if(conn == null) {
conn = new MySQLConnection();
}

return conn;

}

public Connection getConnection() {
return sqlConn;
}

public void closeConnection() {

try{
conn.close();
} catch(SQLException ex) {
ex.printStackTrace();
}
}
}

Que legal, agora temos uma bela implementação de um Singleton!

Opa, problemas a vista…

Bom, o Singleton realmente garante UMA instância na memória. Mas há um problema. Pense o que poderia acontecer se houvessem várias Threads disputando essa instancia. Mas espera! Podemos sincronizar o método! Threads não serão mais problemas de agora em diante!

Bom, eu sou chato. A primeira abordagem resolve o problema das Threads (apesar de causar um overhead a cada chamada do método Singleton), mas ainda há um problema. O Singleton é único POR CLASSLOADER. E o que isso implica? Pensando num ambiente JEE, temos o nosso container JEE (Tomcat, JBoss, Weblogic, Glassfish, etc…) que segue a especificação… advinha… JEE! Mas como o JEE é uma especificação, ele não diz COMO deve ser a implementação. Se seu Container Web favorito internamente aloca diversos classloaders, por performance talvez (sei lá, essa possibilidade não é tão remota assim), isso quer dizer que teremos uma instancia de seu Singleton para cada classloader. Pronto, quebrou nosso esquema. Outro cenário em que temos esse problema é num cluster, onde cada máquina contém uma Virtual Machine diferente, e por consequência, um classloader diferente para cada JVM.

Não bastando esses problemas, e pensando agora no conceito de Orientação a Objetos, porque queremos ter um objeto de instancia única no sistema? Será que existe algum desses no mundo real?

Não sei se é tão vantajoso assim usarmos Singleton, apesar de que em muitos casos ele é uma solução simples que resolve o problema (nem sempre teremos clusters, nem multiplos classloaders, ou estaremos num ambiente JEE de verdade!). Uma busca no google por “singleton anti pattern” pode nos trazer muito mais assunto sobre isso.

Acabou?

Sim, a parte 1 termina aqui, onde vimos como e porque usar (e não usar) o Singleton. Recomendo fortemente uma leitura em artigos na web sobre “singleton anti pattern” e “singletonitis”, ao qual serão muito interessantes em questão de esclarecer todo esse “preconceito” dirigido a esse pattern.

Abraços a todos e espero vocês na parte 2.

Olá pessoal,

Devido a correria de sempre fiquei fora daqui por uns tempos, mas estou voltando para postar sobre um mini-projeto que venho desenvolvendo há um tempo e só agora pude acabar: o JDaVelha, uma implementação de jogo da velha feito com Java que pode ser jogado em rede.

Espero que gostem e que o projeto lhes seja útil de alguma maneira. Abraços.

[EDIT] Para aqueles que desejarem baixar o código fonte, deverão ter o subversion instalado. O projeto foi desenvolvido com o Java 1.6.0_16-b01 no NetBeans 6.7.1.

Endereço: http://jdavelha.googlecode.com/svn/trunk jdavelha-read-only

Categorias

Inscreva-se

Junte-se a 3 outros assinantes