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í?