A ameaça dos singletons
Wednesday, September 12th, 2007Há muito tempo que ouvia sobre os problemas que o padrão singleton representa. É claro que com tantos argumentos não tinha como eu não concordar. Mas eu concordava sem dar muita importância pois nunca havia sentido na pele seus malefícios. Até que finalmente passei por uma grande dificuldade causada por um singleton. Isso finalmente despertou meu ódio por este abominável padrão. Neste artigo explico qual o problema e como o resolvi.
Ontem no desenvolvimento do Menu do GloboVideos 4.2, resolvi exercitar a prática de TDD, ou seja, eu implementeria os testes unitários para guiar o desenvolvimento das classes. O problema é que a classe que representaria o Menu do GloboVideos precisaria utilizar a classe MenuBO (com.globo.portal.cda.menu.business.MenuBO) do Portal.
A idéia era simples, mocar o MenuBO, para isolar o acesso ao banco da minha classe de forma a só testar as regras de negócio do menu do GloboVideos. Mas isso não foi possível, pois esta classe, meus amigos, segue o padrão singleton. Ou seja, o construtor dela é privado, o que impossibilita a criação de Mocks e de SubClasses.
A solução que encontrei foi criar uma classe Proxy. A MenuBOProxy. Não sem antes testar de várias maneiras um jeito de criar mocks com classes de construtor privado. Não consegui. A classe MenuBOProxy apenas implementa todos os métodos de MenuBO, delegando cada uma delas a uma instância do objeto único de MenuBO. Veja como a classe ficou:
public class MenuBOProxy {
private MenuBO menuBo;
public MenuBOProxy() {
this.menuBo = MenuBO.getInstance();
}
public MenuBOProxy(MenuBO menuBo) {
this.menuBo = menuBo;
}
public Vector listaItem(MenuVO menu)
throws BusinessException {
return menuBo.listaItem(menu);
}
public MenuVO listaMenu(MenuVO menu)
throws BusinessException {
return menuBo.listaMenu(menu);
}
public Vector listaMenus()
throws BusinessException {
return menuBo.listaMenus();
}
public String retornaListaNomeMenu(Vector vetMenuId)
throws BusinessException {
return menuBo.retornaListaNomeMenu(vetMenuId);
}
public MenuVO listaMenuPorPortalNome(MenuVO menu)
throws BusinessException {
return menuBo.listaMenuPorPortalNome(menu);
}
public MenuVO listaMenuPorId(MenuVO menu)
throws BusinessException {
return menuBo.listaMenuPorPortalNome(menu);
}
}
Com esse proxy foi possivel criar um mock e enfim testar separadamente a lógica do Menu do GloboVideos.
Por hora, a versão atual do GloboVideos também foi desenvolvido utilizando-se singletons seguindo o padrão do Portal. Mas aos poucos estamos migrando suas funcionalidades de forma a permitir uma maior testabilidade no código. Sempre seguindo a máxima de que o padrão singleton é mal, pega um pega geral.
Outros links com mais argumentos e exemplos disso estão disponíveis abaixo:
http://blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx
http://c2.com/cgi/wiki?SingletonsAreEvil
http://www.softwarereality.com/design/singleton.jsp
http://members.capmac.org/~orb/blog.cgi/tech/coding/no_singletons.writeback
http://www.testingreflections.com/node/view/552