Middle+
Por que o padrão Singleton é considerado um anti-padrão?
sobes.tech IA
Resposta da IA
Singleton viola os princípios SOLID e complica os testes:
- Violação do princípio de responsabilidade única (SRP): A classe é responsável tanto pela sua lógica quanto por gerir o seu ciclo de vida (criação e acesso à instância única).
- Violação do princípio de aberto/fechado (OCP): Expandir a funcionalidade da classe Singleton pode ser difícil sem modificar o seu código.
- Violação do princípio de substituição de Liskov (LSP): Os subtipos da classe Singleton podem não cumprir os contratos do tipo base devido às particularidades da implementação Singleton.
- Violação do princípio de inversão de dependências (DIP): Os módulos dependem de uma implementação concreta de Singleton, não de abstrações. Isto torna difícil substituir o objeto Singleton por um mock ou stub para testes.
- Problemas com os testes: A instância Singleton é um estado global, o que dificulta a execução paralela de testes e o isolamento de cenários de teste. É difícil substituir as dependências do objeto Singleton.
- Dependências ocultas: O uso de Singleton oculta as dependências entre módulos, pois elas não são passadas explicitamente.
- Problemas de concorrência: A necessidade de sincronizar o acesso à instância Singleton em ambientes multithread pode complicar o código e gerar erros potenciais.
- Complexidade na eliminação: A gestão do ciclo de vida do objeto Singleton e a sua eliminação podem não ser evidentes.
Em vez de Singleton, é frequentemente preferível usar injeção de dependências ou um Localizador de Serviços para gerir o ciclo de vida dos objetos e o seu acesso.
Exemplo de uma implementação típica de Singleton em Swift que demonstra os problemas mencionados:
// Singleton.swift
class DataManager {
static let shared = DataManager() // Ponto de acesso global, oculta a dependência
private init() { // Construtor privado, dificulta a criação de objetos mock
// Inicialização de recursos ou estado
}
func fetchData() -> [String] {
// Lógica para obter dados (por exemplo, a partir da rede ou base de dados)
// Esta lógica está relacionada com a gestão do estado e o acesso, violando SRP
return ["Item 1", "Item 2"]
}
}
// Exemplo de uso
// Módulo que depende diretamente de DataManager.shared
class ViewController {
func displayData() {
let data = DataManager.shared.fetchData() // Dependência oculta
print(data)
}
}
// Os testes de ViewController tornam-se difíceis:
// Como substituir DataManager.shared por um objeto mock?
// Como isolar os testes se DataManager.shared é global?