Sobes.tech
Middle+

Prečo je vzorec Singleton považovaný za anti-vzor?

sobes.tech AI

Odpoveď od AI

Singleton porušuje princípy SOLID a sťažuje testovanie:

  • Porušenie princípu jednotnej zodpovednosti (SRP): Trieda súčasne zodpovedá za svoju logiku a za správu svojho životného cyklu (vytváranie a prístup k jedinému inštancii).
  • Porušenie princípu otvorenosti/zatvorenosti (OCP): Rozšírenie funkčnosti Singleton triedy môže byť sťažené bez zmeny jej kódu.
  • Porušenie princípu zámene Barbare Liskov (LSP): Podtypy Singleton triedy nemusia spĺňať zmluvy základného typu kvôli špecifikám implementácie Singleton.
  • Porušenie princípu inverzie závislostí (DIP): Moduly závisia od konkrétnej implementácie Singleton, nie od abstrakcií. To sťažuje nahradenie Singleton objektu s mock alebo stub pre testovanie.
  • Problémy s testovaním: Singleton inštancia je globálny stav, čo sťažuje paralelné vykonávanie testov a izoláciu testovacích scenárov. Ťažko je nahradiť závislosť od Singleton objektu.
  • Skryté závislosti: Použitie Singleton skrýva závislosti medzi modulmi, pretože nie sú explicitne odovzdané.
  • Problémy s viacerými vláknami: Potreba synchronizácie prístupu k Singleton v viacvláknovej prostredí môže viesť k zložitostiam v kóde a potenciálnym chybám.
  • Zložitosť pri likvidácii: Riadenie životného cyklu Singleton objektu a jeho likvidácia môže byť nejasná.

Namiesto Singleton je často vhodnejšie použiť injekciu závislostí (Dependency Injection) alebo Service Locator na správu životného cyklu objektov a ich prístupu.

Príklad typickej implementácie Singleton v Swift, ktorý demonštruje spomenuté problémy:

// Singleton.swift
class DataManager {
    static let shared = DataManager() // Globálny bod prístupu, skrýva závislosť
    
    private init() { // Uzavretý konštruktor, sťažuje vytváranie mock objektov
        // Inicializácia zdrojov alebo stavu
    }
    
    func fetchData() -> [String] {
        // Logika získavania dát (napríklad zo siete alebo databázy)
        // Táto logika je spojená s riadením stavu a prístupom, porušuje SRP
        return ["Item 1", "Item 2"]
    }
}

// Príklad použitia
// Modul, ktorý priamo závisí od DataManager.shared
class ViewController {
    func displayData() {
        let data = DataManager.shared.fetchData() // Skrytá závislosť
        print(data)
    }
}

// Testovanie ViewController je zložitejšie:
// Ako nahradiť DataManager.shared mock objektom?
// Ako izolovať testy, ak je DataSourceManager.shared globálny?