Sobes.tech
Middle+

Waarom wordt het Singleton-patroon als een anti-patroon beschouwd?

sobes.tech AI

Antwoord van AI

Singleton schendt de SOLID-principes en bemoeilijkt het testen:

  • Schending van het Single Responsibility Principle (SRP): De klasse is verantwoordelijk voor zowel haar eigen logica als voor het beheer van haar levenscyclus (creatie en toegang tot de enkele instantie).
  • Schending van het Open/Closed Principle (OCP): Het uitbreiden van de functionaliteit van de Singleton-klasse kan moeilijk zijn zonder de code te wijzigen.
  • Schending van het Liskov Substitutie Principe (LSP): Subtypen van de Singleton-klasse voldoen mogelijk niet aan de contracten van het basistype vanwege de bijzonderheden van de Singleton-implementatie.
  • Schending van het Dependency Inversion Principle (DIP): Modules vertrouwen op een concrete implementatie van Singleton, niet op abstracties. Dit maakt het moeilijk om het Singleton-object te vervangen door een mock of stub voor tests.
  • Problemen met testen: De Singleton- instantie is een globale staat, wat het parallel uitvoeren van tests en het isoleren van testscenario's bemoeilijkt. Het is moeilijk om afhankelijkheden van het Singleton-object te vervangen.
  • Verborgen afhankelijkheden: Het gebruik van Singleton verbergt de afhankelijkheden tussen modules, omdat ze niet expliciet worden doorgegeven.
  • Multithreading-problemen: De noodzaak om toegang tot de Singleton-instantie te synchroniseren in een multithread-omgeving kan de code compliceren en tot potentiële fouten leiden.
  • Complexiteit van het afhandelen: Het beheer van de levenscyclus van het Singleton-object en de verwijdering ervan kunnen onduidelijk zijn.

In plaats van Singleton wordt het vaak aanbevolen om afhankelijkheidsinjectie of een Service Locator te gebruiken voor het beheer van de levenscyclus en toegang tot objecten.

Een voorbeeld van een typische Singleton-implementatie in Swift die de genoemde problemen illustreert:

// Singleton.swift
class DataManager {
    static let shared = DataManager() // Globale toegangspunt, verbergt afhankelijkheid
    
    private init() { // Privé-constructeur, maakt het moeilijk om mock-objecten te maken
        // Initialisatie van bronnen of toestand
    }
    
    func fetchData() -> [String] {
        // Logica voor het ophalen van gegevens (bijvoorbeeld uit netwerk of database)
        // Deze logica is gerelateerd aan het beheer van toestand en toegang, en schendt SRP
        return ["Item 1", "Item 2"]
    }
}

// Voorbeeld van gebruik
// Module die direct afhankelijk is van DataManager.shared
class ViewController {
    func displayData() {
        let data = DataManager.shared.fetchData() // Verborgen afhankelijkheid
        print(data)
    }
}

// Testen van ViewController wordt moeilijk:
// Hoe kan DataManager.shared worden vervangen door een mock-object?
// Hoe kunnen tests worden geïsoleerd als DataManager.shared globaal is?