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?