Sobes.tech
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?