Wzorzec singleton (pojedynczy obiekt) to popularna technika tworzenia obiektu, który istnieje w jedynym egzemplarzu w aplikacji. W Swift implementacja wzorca została uproszczona dzięki wsparciu dla statycznych właściwości i bezpieczeństwa wątkowego.
Historia pytania:
W Objective-C realizacja Singletona wymagała długiego i nietrywialnego kodu związanego z bezpieczeństwem wątkowym. W Swift problem ten został rozwiązany dzięki leniwej inicjalizacji statycznych właściwości.
Problem:
Singleton często wykorzystuje się do centralnego dostępu do stanu aplikacji (np. Sesja, konfiguracje, usługi), ale niewłaściwe użycie prowadzi do niejawnych zależności i obniżonej testowalności kodu.
Rozwiązanie:
W Swift wzorzec realizuje się przez statyczną stałą typu. Gwarantuje to bezpieczeństwo wątkowe i jednoznaczność:
Przykład kodu:
final class Logger { static let shared = Logger() private init() {} func log(_ message: String) { print(message) } } Logger.shared.log("Przykład działania Singletona")
Kluczowe cechy:
Czy można (i czy należy) zawsze używać singletona dla wszystkich usług aplikacji?
Nie. Singleton jest uzasadniony dla prawdziwie globalnego stanu (np. ApplicationSettings), ale jego użycie dla usług logiki biznesowej jest niewłaściwe: pojawiają się ścisłe powiązania i problemy z testowaniem jednostkowym.
Czy static let zapewnia bezpieczną inicjalizację Singletona w Swift?
Tak, poczynając od Swift 1.2 (i z powodu architektury runtime), static let jest z natury bezpieczny dla wątków — inicjowany jest tylko raz nawet przy współzawodnictwie wątków.
Czy singleton może być dziedziczony?
Nie. Lepiej zadeklarować klasę jako final, aby uniknąć dziedziczenia — w przeciwnym razie możliwe jest utworzenie dwóch egzemplarzy singletonów różnych klas-dziedziczących, co narusza samą ideę wzorca.
Usługa pracy z siecią zrealizowana przez singleton, a metody korzystają z globalnego stanu. W testach jednostkowych pojawia się niejawna zależność, niemożliwe jest ponowne wykorzystanie usługi.
Zalety:
Wady:
Singleton używany jest tylko dla prawdziwie globalnej encji — np. ApplicationConfig. Wszystkie usługi otrzymują zależności za pomocą jawnego wstrzyknięcia.
Zalety:
Wady: