Informações
| Tipo: | Artigo |
|---|---|
| Data de Publicação: | 31/12/2005 |
| Revisado em: | 31/12/2005 |
Vote!
Tags Relacionadas
Comentários ( 11 )
Imprimir
Padrões de Projeto: O padrão Singleton
por:
David Pereira (david@jeebrasil.com.br)
Este é o primeiro de uma série de artigos sobre Padrões de Projeto, os Design Patterns. Neste artigo, será apresentado o padrão Singleton. O Singleton é um dos padrões de projeto mais simples e é usado quando for necessária a existência de apenas uma instância de uma classe.
1. Introdução
Este é o primeiro de uma série de artigos sobre Padrões de Projeto, os Design Patterns . Faz-se então necessária uma breve introdução sobre o assunto para que o leitor possa acompanhar este e os demais artigos consciente do que trata o seu objeto de estudo.
Considera-se como o início da história dos padrões de projeto a publicação da obra A Pattern Language: Towns, Buildings, Construction [1], de Christopher Alexander, em 1987. Entretanto, a sua popularização só veio oito anos depois, em 1995, com o livro Design Patterns – Elements of Reusable Object-Oriented Software [2], também conhecido como GoF, de Erich Gamma et al.
Um Padrão de Projeto pode ser definido como “a solução para um problema em um contexto” [3]. Isto significa que os padrões de projeto nos oferecem soluções prontas para utilizarmos em determinados problemas que podemos enfrentar quando desenvolvemos um software orientado a objetos.
Os padrões de projeto são observados através da experiência de um desenvolvedor com projetos de software, o que significa que os padrões são descobertos, e não inventados. Como eles fornecem soluções prontas para a solução de problemas, existem diversas vantagens no seu uso, como a reutilização de códigos, modelos e arquiteturas, a padronização de designs e boas práticas, a criação de uma linguagem comum entre desenvolvedores, entre outras.
Podemos classificar os padrões de projeto de várias maneiras, porém o mais comum é classificá-los de acordo com os tipos de problemas que eles resolvem. De acordo com esse critério, os padrões podem ser: de criação , que lidam com o problema da criação de objetos; estruturais , que lidam com os relacionamentos entre objetos; comportamentais , que lidam com a atribuição de responsabilidades aos objetos; e outros.
Ao descrever um padrão de projeto devemos especificar o seu nome, que o identifica e descreve; o contexto , ou seja, a situação na qual se deve aplicar o padrão; o problema , que é a meta que está se tentando atingir neste contexto; a solução proposta pelo padrão para o problema no contexto especificado e as conseqüências da aplicação do padrão, podendo serem elas boas ou ruins. Para compreender melhor esses conceitos, veja o exemplo abaixo, que não pode ser considerado um padrão de projeto, servindo apenas para ajudar a compreender o modo como um padrão de projeto deve ser definido.
Nome: Quebra-Janelas!
Problema : Como chegar cedo ao trabalho?
Contexto : Tranquei o meu carro com as chaves dentro!
Solução : Quebre a janela, entre no carro e dirija até o trabalho.
Conseqüências : Uma janela quebrada e a mão doendo.
Os padrões de projeto solucionam muitos dos problemas que os projetistas enfrentam no dia a dia, e de muitas maneiras diferentes [2]. Nesta série de artigos tentaremos mostrar alguns dos padrões mais comuns, explicando o contexto, o problema, a solução e suas conseqüências com exemplos simples e acessíveis. Todos os exemplos estarão escritos em Java, mas são aplicáveis em qualquer linguagem orientada a objetos.
<%-- PARTE 2 --%>2 . O padrão Singleton
O Singleton é um dos padrões de projeto mais simples. Ele é usado quando for necessária a existência de apenas uma instância de uma classe. Foi apresentado no GoF [2] como um padrão de criação, por lidar com a criação de objetos.
O GoF nos diz que para uma classe ser um singleton, deve-se garantir que haverá apenas uma instância na aplicação e que deve-se fornecer um ponto de acesso à mesma. Mas como garantir que haverá apenas uma instância? É de conhecimento comum que, para criar uma instância de uma classe, devemos chamar o seu construtor. Assim, para resolvermos o problema, devemos restringir o acesso ao construtor, tornando-o um método privado. Em seguida, deve-se utilizar um método público que faça o controle da instanciação, de modo que ela só possa ser feita uma vez. A Figura 1 mostra o diagrama de classes do singleton:
Figura 1: Diagrama de Classes do Singleton.
A implementação mais comum do singleton pode ser vista no código apresentado na Listagem 1.
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
Listagem 1 : Implementação do Singleton .
O diagrama de classes da Figura 1 e o código da Listagem 1 mostram apenas o necessário para a instanciação do singleton. Obviamente, a classe deve conter outros atributos e métodos.
A Figura 2 mostra um diagrama de seqüências que representa o processo de criação de um singleton. Quando um objeto chama o singleton pela primeira vez, é realizada a instanciação da classe. Os demais acessos irão utilizar a instância já criada.
Figura 2: Diagrama de Seqüências de um Singleton.
O código da Listagem 1 pode ser problemático em ambientes multi-threaded, ou seja, ele não é uma solução thread-safe. Se uma thread chamar o método getInstance() e for interrompida antes de realizar a instanciação, uma outra thread poderá chamar o método e realizar a instanciação. Neste caso, duas instâncias serão construídas, o que fere os requisitos do singleton . Uma solução para este problema seria utilizar o atributo synchronized em getInstance(), como visto na Listagem 2 , para que uma outra thread não possa acessá-lo até que a thread que o acessou pela primeira vez tenha terminado de fazê-lo.
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
Listagem 2 : Implementação thread-safe com synchronized./p>
O problema com o synchronized é que a sincronização é bastante custosa. Estima-se que métodos sincronizados sejam cerca de cem vezes mais lentos que métodos não sincronizados [3]. Uma alternativa simples, rápida e thread-safe é a instanciação do singleton assim que ele for declarado. Veja a Listagem 3 .
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
Listagem 3 : Implementação thread-safe com instanciação estática.
3. Conclusões
Além de ser um dos padrões mais simples, o singleton é também um dos mais criticados e mal usados. Uma situação em que realmente é necessário que exista apenas uma instância de uma classe é difícil e o seu uso pode levar a muitos problemas. O uso abusivo de singletons leva a soluções onde a dependência entre objetos é muito forte e a testabilidade é fraca. Adicionalmente, os singletons são difíceis de escalar, pois é difícil garantir uma única instância de um singleton em um cluster, onde existem várias JVMs. Um outro problema é que os singletons dificultam o hot redeploy por permanecerem em cache, bloqueando mudanças de configuração. Por esses e outros problemas, deve-se ter cuidado com o uso abusivo de singletons.
4. Referências
[1] Alexander, Christopher. A Pattern Language: Towns, Buildings, Construction . Oxford University Press, 1987.
[2] Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. Padrões de Projeto – Soluções reutilizáveis de Software Orientado a Objetos . Bookman, 2004.
[3] Freeman, Eric; Freeman, Elisabeth. Head First Design Patterns . O'Reilly, 2004.
Comentários (11)
- Bastante interessante, mas ficou faltando um exemplo onde esse Padrão seja realmente necessário.
- postado por Pedro Zatta em 05/10/2006 às 23:21
-
É verdade, Pedro. Ficou faltando. Vou tentar então complementar aqui. É comum o uso de singletons em classes que servem para fornecer recursos, como por exemplo uma classe que pega conexões do banco de dados ou uma classe Factory (Factory é um padrão de projeto que iremos ver em um artigo futuro).
Nesses casos, usa-se o singleton para termos apenas uma classe cuidando das conexões (ou da criação de instâncias, no caso do factory) e podermos ter mais controle sobre essa criação de conexões. Quando usa-se o singleton aqui é porque quer-se centralizar em apenas uma instância a criação das conexões.
Claro que essa não é a única abordagem possível para resolver esse problema, mas é um exemplo comum da utilização do singleton.
- postado por David Pereira em 07/10/2006 às 23:21
- Olá David, Parabéns pela matéria, sou programador Delphi e estou iniciando com Patterns em Java, seria possível mostrar mais aplicações práticas e com exemplos bem didáticos para que pessoas como eu, que também estão iniciando possam melhor compreender sobre o Singleton ? Abraços e obrigado
- postado por Gilson em 13/10/2006 às 23:21
- Ola, Primeiramente, parabens pela didatica, muito bem explicado. Estava aplicando este padrao em uma aplicacao web, no que tange a aproveitar a conexao JDBC com o BD Mysql. Estou com duvida será que se um usuario der o commit enquando outro der o rollback, como os dois usam a mesma conexao pelo tomcat, nao vai dar problema ? Grato, Helder
- postado por Helder em 28/10/2006 às 23:21
- Helder, os dois não aproveitariam a mesma conexão não. O que eu falei que é comum ver gente fazendo é deixar um singleton fazendo a criação de novas conexões, mas as conexões não seriam singletons.
- postado por David Pereira em 31/10/2006 às 23:21
- De todos os artigos que achei sobre Singleton, este é o mais simples de se entender. Parabéns.
- postado por Fernanda Angelo em 05/12/2006 às 23:21
- Utlizei esse pattern na minha classe ConnectionFactory em minha aplicação. Dessa maneira, haverá apenas uma conexão válida, para a aplicação inteira.
- postado por Muito Boa Essa Matéria em 12/01/2007 às 23:21
- Olá.. parebéns pela matéria..bom.. há uma boa prática que foi esquecida nesta classe Singleton.. se você não colocar a classe como final será possivel clonar a classe. Ou seja, todos os objetos comuns estendem de Object, e object implementa cloneable.. no caso se a sua classe singleton não for final o método clone poderá ser acessado quebrará a segurança proposta pelo singleton..
- postado por Luciano Rafael em 12/02/2007 às 23:21
- Gosto muito dos artigos e tutoriais aqui apresentados. Sua linguagem e exemplos são bem esplicativos e de fácil entendimento. Mas não entendo por que não disponibilizar em PDF. Por que não ?
- postado por Alessandro Bezerra Moreira em 17/05/2007 às 23:21
- Olá David, gostei muito desse tutorial, e simples e objetivo. Por favor me tire uma dúvida, com base nos conceitos que vc passou a listagem 3 não apresentaria problemas? Da forma que foi implementado sempre que o construtor da chamado e criará uma nova instância. Não seria necessário utilizar a mesma estratégia de criação que vc utilizou nas listagem 1 e 2? Abraço.
- postado por Cirismar Coutinho em 06/06/2007 às 23:21
- Muito bom o artigo. Sugiro que sejam colocados exemplos corretos e incorretos.
- postado por Luciano em 12/07/2007 às 23:21
