Singleton - Anti Pattern?
E aí, devs! Apesar da polêmica em torno do Singleton — considerado por alguns um anti-pattern —, primeiro temos que entender o que ele é e para que serve, para então tirarmos nossas próprias conclusões. Recentemente, ao implementar um logger em uma aplicação, me deparei novamente com o Design Pattern Singleton. Então, pensei em escrever este mini artigo para compartilhar o que aprendi durante meus estudos e minha experiência com esse padrão de design. Por definição: O Singleton é um Creational Pattern criado para resolver problemas na criação de objetos. Ele garante que apenas uma instância de determinado objeto exista e fornece um único ponto de acesso a ela para qualquer outro código. Mas em que isso realmente ajuda? Principalmente em evitar a criação desnecessária de instâncias de objetos que são frequentemente utilizados em nossa aplicação, reduzindo assim o consumo de memória do sistema. Isso é ótimo, não é? Sim, mas (sempre tem um "mas") isso nem sempre acontece automaticamente. Para que a implementação funcione corretamente, é preciso considerar alguns pontos e contextos específicos aos quais você deve estar atento. Primeiro, vamos entender como o Singleton é implementado em uma aplicação. Deve prover um único ponto de criação Deve fornecer acesso global Deve gerenciar seu próprio ciclo de vida E há mais um ponto a ser levado em consideração: se a sua linguagem trabalha com multithreading, é essencial ter cuidado com a sincronização na criação e no acesso ao Singleton para evitar problemas de concorrência. Aqui já podemos pensar em alguns cenários onde a implementação do Singleton seria vantajosa para a aplicação, como: Logger, garantindo que todas as partes do sistema utilizem a mesma instância para registrar logs. Instâncias de conexão com o banco de dados, evitando a criação desnecessária de múltiplas conexões. Compartilhamento de recursos de hardware, como drivers de impressão ou acesso. Assim como os demais Design Patterns, o Singleton possui tanto pontos positivos quanto negativos a serem considerados: Podemos notar que compartilhar recursos, controlar o acesso e realizar operações singulares são os pontos positivos do Singleton. No entanto, há também alguns pontos negativos a serem considerados, como a testabilidade. Isso ocorre devido à criação de dependências ocultas, o que resulta em acoplamento. A violação do "S" do S.O.L.I.D. também é um ponto recorrente na implementação do Singleton, justamente por causa desse acoplamento de dependências. Além disso, se no futuro você precisar de múltiplas instâncias desse Singleton, terá que refatorar o código, o que pode ser um problema. No final, tudo se resume ao trade-off: é uma troca. Por exemplo, no caso de memory leaks, a escolha entre criar um novo objeto ou manter um objeto existente depende de qual opção é mais vantajosa no contexto da sua aplicação. Então, o Singleton é um amigo ou inimigo? Tudo vai depender do contexto da sua aplicação, mas eu gosto de dizer que ele é um bom amigo quando é necessário. Este é um artigo mais teórico, então não vou me aprofundar na implementação nas linguagens, visto que há muitos exemplos facilmente disponíveis na internet. Obrigado pela leitura até agora! Espero ter ajudado e até a próxima! Refêrencias Lucas Montano - Singleton como nunca antes explicado - Design Pattern Opus-software - Singleton Design Pattern Gabi Deuther - O uso do Singleton Design Pattern Refactoring Guru - Singleton

E aí, devs!
Apesar da polêmica em torno do Singleton — considerado por alguns um anti-pattern —, primeiro temos que entender o que ele é e para que serve, para então tirarmos nossas próprias conclusões.
Recentemente, ao implementar um logger em uma aplicação, me deparei novamente com o Design Pattern Singleton. Então, pensei em escrever este mini artigo para compartilhar o que aprendi durante meus estudos e minha experiência com esse padrão de design.
Por definição:
O Singleton é um Creational Pattern criado para resolver problemas na criação de objetos. Ele garante que apenas uma instância de determinado objeto exista e fornece um único ponto de acesso a ela para qualquer outro código.
Mas em que isso realmente ajuda?
Principalmente em evitar a criação desnecessária de instâncias de objetos que são frequentemente utilizados em nossa aplicação, reduzindo assim o consumo de memória do sistema. Isso é ótimo, não é?
Sim, mas (sempre tem um "mas") isso nem sempre acontece automaticamente. Para que a implementação funcione corretamente, é preciso considerar alguns pontos e contextos específicos aos quais você deve estar atento.
Primeiro, vamos entender como o Singleton é implementado em uma aplicação.
- Deve prover um único ponto de criação
- Deve fornecer acesso global
- Deve gerenciar seu próprio ciclo de vida
E há mais um ponto a ser levado em consideração: se a sua linguagem trabalha com multithreading, é essencial ter cuidado com a sincronização na criação e no acesso ao Singleton para evitar problemas de concorrência.
Aqui já podemos pensar em alguns cenários onde a implementação do Singleton seria vantajosa para a aplicação, como:
- Logger, garantindo que todas as partes do sistema utilizem a mesma instância para registrar logs.
- Instâncias de conexão com o banco de dados, evitando a criação desnecessária de múltiplas conexões.
- Compartilhamento de recursos de hardware, como drivers de impressão ou acesso.
Assim como os demais Design Patterns, o Singleton possui tanto pontos positivos quanto negativos a serem considerados:
Podemos notar que compartilhar recursos, controlar o acesso e realizar operações singulares são os pontos positivos do Singleton. No entanto, há também alguns pontos negativos a serem considerados, como a testabilidade. Isso ocorre devido à criação de dependências ocultas, o que resulta em acoplamento.
A violação do "S" do S.O.L.I.D. também é um ponto recorrente na implementação do Singleton, justamente por causa desse acoplamento de dependências. Além disso, se no futuro você precisar de múltiplas instâncias desse Singleton, terá que refatorar o código, o que pode ser um problema.
No final, tudo se resume ao trade-off: é uma troca. Por exemplo, no caso de memory leaks, a escolha entre criar um novo objeto ou manter um objeto existente depende de qual opção é mais vantajosa no contexto da sua aplicação.
Então, o Singleton é um amigo ou inimigo? Tudo vai depender do contexto da sua aplicação, mas eu gosto de dizer que ele é um bom amigo quando é necessário.
Este é um artigo mais teórico, então não vou me aprofundar na implementação nas linguagens, visto que há muitos exemplos facilmente disponíveis na internet.
Obrigado pela leitura até agora! Espero ter ajudado e até a próxima!
Refêrencias
- Lucas Montano - Singleton como nunca antes explicado - Design Pattern
- Opus-software - Singleton Design Pattern
- Gabi Deuther - O uso do Singleton Design Pattern
- Refactoring Guru - Singleton