Middle+
¿Por qué se considera que el patrón Singleton es un anti patrón?
sobes.tech AI
Respuesta de la IA
Singleton viola los principios SOLID y complica las pruebas:
- Violación del principio de responsabilidad única (SRP): La clase es responsable tanto de su lógica como de gestionar su ciclo de vida (creación y acceso a la instancia única).
- Violación del principio de abierto/cerrado (OCP): Ampliar la funcionalidad de la clase Singleton puede ser difícil sin modificar su código.
- Violación del principio de sustitución de Liskov (LSP): Los subtipos de la clase Singleton pueden no cumplir con los contratos del tipo base debido a las particularidades de la implementación Singleton.
- Violación del principio de inversión de dependencias (DIP): Los módulos dependen de una implementación concreta de Singleton, no de abstracciones. Esto hace difícil reemplazar el objeto Singleton por un mock o stub para pruebas.
- Problemas con las pruebas: La instancia Singleton es un estado global, lo que dificulta la ejecución paralela de pruebas y la aislamiento de escenarios de prueba. Es difícil sustituir las dependencias del objeto Singleton.
- Dependencias ocultas: El uso de Singleton oculta las dependencias entre módulos, ya que no se pasan explícitamente.
- Problemas con la concurrencia: La necesidad de sincronizar el acceso a la instancia Singleton en entornos multihilo puede complicar el código y generar errores potenciales.
- Complejidad en la eliminación: La gestión del ciclo de vida del objeto Singleton y su eliminación puede no ser evidente.
En lugar de Singleton, a menudo es preferible usar inyección de dependencias o un Localizador de Servicios para gestionar el ciclo de vida de los objetos y su acceso.
Ejemplo de una implementación típica de Singleton en Swift que demuestra los problemas mencionados:
// Singleton.swift
class DataManager {
static let shared = DataManager() // Punto de acceso global, oculta la dependencia
private init() { // Constructor privado, dificulta crear objetos mock
// Inicialización de recursos o estado
}
func fetchData() -> [String] {
// Lógica para obtener datos (por ejemplo, desde la red o base de datos)
// Esta lógica está relacionada con la gestión del estado y el acceso, violando SRP
return ["Item 1", "Item 2"]
}
}
// Ejemplo de uso
// Módulo que depende directamente de DataManager.shared
class ViewController {
func displayData() {
let data = DataManager.shared.fetchData() // Dependencia oculta
print(data)
}
}
// Las pruebas de ViewController se vuelven difíciles:
// ¿Cómo reemplazar DataManager.shared por un objeto mock?
// ¿Cómo aislar las pruebas si DataManager.shared es global?