Sobes.tech
Middle+

Proč je vzorec Singleton považován za anti-vzor?

sobes.tech AI

Odpověď od AI

Singleton porušuje principy SOLID a komplikuje testování:

  • Porušení principu jediné odpovědnosti (SRP): Třída je odpovědná jak za svou logiku, tak za správu svého životního cyklu (vytváření a přístup k jediné instanci).
  • Porušení principu otevřenosti/zavřenosti (OCP): Rozšíření funkčnosti třídy Singleton může být obtížné bez změny jejího kódu.
  • Porušení principu nahrazování Liskov (LSP): Podtypy třídy Singleton nemusí splňovat smlouvy základního typu kvůli zvláštnostem implementace Singleton.
  • Porušení principu obrácení závislostí (DIP): Moduly závisí na konkrétní implementaci Singleton, nikoliv na abstrakcích. To ztěžuje nahrazení objektu Singleton mockem nebo stubem při testování.
  • Problémy s testováním: Instanci Singleton je globální stav, což ztěžuje paralelní provádění testů a izolaci scénářů testů. Je obtížné nahradit závislosti objektu Singleton.
  • Skryté závislosti: Použití Singleton skrývá závislosti mezi moduly, protože nejsou předávány explicitně.
  • Problémy s vícenásobným vláknem: Nutnost synchronizace přístupu k instanci Singleton v prostředí s více vlákny může vést ke komplikacím a potenciálním chybám.
  • Složitost likvidace: Správa životního cyklu objektu Singleton a jeho likvidace nemusí být zřejmá.

Místo Singleton je často vhodnější použít injekci závislostí nebo Service Locator pro správu životního cyklu objektů a jejich přístupu.

Příklad typické implementace Singleton v Swift, který demonstruje zmíněné problémy:

// Singleton.swift
class DataManager {
    static let shared = DataManager() // Globální přístupový bod, skrývá závislost
    
    private init() { // Soukromý konstruktor, ztěžuje tvorbu mock objektů
        // Inicializace zdrojů nebo stavu
    }
    
    func fetchData() -> [String] {
        // Logika získávání dat (například z sítě nebo databáze)
        // Tato logika je spojena s řízením stavu a přístupem, porušuje SRP
        return ["Item 1", "Item 2"]
    }
}

// Příklad použití
// Modul, který přímo závisí na DataManager.shared
class ViewController {
    func displayData() {
        let data = DataManager.shared.fetchData() // Skrytá závislost
        print(data)
    }
}

// Testování ViewController je složité:
// Jak nahradit DataManager.shared mock objektem?
// Jak izolovat testy, pokud je DataManager.shared globální?