Menu
Coddy logo textTech

Wzorzec Singleton

Część sekcji Programowanie obiektowe ścieżki Swift w Coddy. Lekcja 42 z 57.

Wzorzec projektowy to sprawdzone rozwiązanie typowego problemu projektowego. Wzorzec singleton gwarantuje, że klasa ma dokładnie jedną instancję, dostępną z dowolnego miejsca. W Swift jest to static let przechowujące instancję oraz private init, dzięki któremu nikt nie może utworzyć kolejnej:

final class Settings {
    static let shared = Settings()
    var theme = "light"

    private init() {}
}

Settings.shared.theme = "dark"
print(Settings.shared.theme)

Wynik:

dark

Każde użycie Settings.shared odnosi się do tego samego obiektu, więc zmiana wprowadzona w jednej części programu jest widoczna wszędzie. static let jest tworzone leniwie przy pierwszym użyciu i bezpiecznie, nawet gdy kilka wątków uruchomi się jednocześnie:

final class Counter {
    static let shared = Counter()
    private(set) var hits = 0
    private init() {}
    func hit() { hits += 1 }
}

func pageA() { Counter.shared.hit() }
func pageB() { Counter.shared.hit(); Counter.shared.hit() }
pageA()
pageB()
print(Counter.shared.hits)
print(Counter.shared === Counter.shared)

Wynik:

3
true

Ponieważ inicjalizator jest prywatny, Settings() nie kompiluje się poza klasą, co pozwala dotrzymać obietnicy „tylko jednej instancji”:

// let other = Settings()   // błąd: inicjalizator 'Settings' jest niedostępny ze względu na poziom dostępu 'private'

Używaj singletonów oszczędnie. Singleton to stan globalny: dowolny kod może go zmienić, a testy nie mogą łatwo rozpocząć działania od czystej kopii. Nadają się do rzeczy, które naprawdę występują tylko raz w programie, takich jak rejestrator lub konfiguracja aplikacji; często czytelniej jest przekazać obiekt jako parametr.

// Łatwiejsze do testowania: logger jest przekazywany jako argument
func process(order: Order, log: Logger) { log.write("processing \(order.id)") }
challenge icon

Wyzwanie

Łatwy

Uzupełnij singleton Logger. Ma instancję static let shared, prywatny inicjalizator, tablicę entries z private(set) oraz log(_:level:), która przechowuje [INFO] message lub [ERROR] message (poziom to info lub error). errorCount jest właściwością obliczaną. Dostarczony kod wywołuje Logger.shared z dwóch różnych funkcji, więc obie muszą uzyskać dostęp do tej samej instancji.

Dostarczony kod odczytuje wiersze info,message lub error,message, przekazuje wiersze informacyjne do handleInfo, a wiersze błędów do handleError, a następnie wyświetla każdy wpis i errors: 1.

Twój kod umieść w plikach Logger.swift i Functions.swift. main.swift zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    let p = line.split(separator: ",", maxSplits: 1).map(String.init)
    if p[0] == "error" { handleError(p[1]) } else { handleInfo(p[1]) }
}
for e in Logger.shared.entries { print(e) }
print("errors: \(Logger.shared.errorCount)")
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Edytor online Swift