Menu
Coddy logo textTech

Singleton-Muster

Teil des Abschnitts Objektorientierte Programmierung der Swift-Journey von Coddy. Lektion 42 von 57.

Ein Entwurfsmuster ist eine bewährte Lösung für ein häufiges Entwurfsproblem. Das Singleton-Muster stellt sicher, dass eine Klasse genau eine Instanz hat, die von überall aus erreichbar ist. In Swift ist es ein static let, das die Instanz enthält, zusammen mit einem private init, sodass niemand eine weitere erstellen kann:

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

    private init() {}
}

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

Ausgabe:

dark

Jede Verwendung von Settings.shared verweist auf dasselbe Objekt, sodass eine Änderung in einem Teil des Programms überall sichtbar ist. static let wird bei der ersten Verwendung verzögert und sicher erstellt, selbst wenn mehrere Threads gleichzeitig starten:

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)

Ausgabe:

3
true

Da der Initialisierer privat ist, lässt sich Settings() außerhalb der Klasse nicht kompilieren, wodurch das Versprechen „nur eines“ eingehalten wird:

// let other = Settings()   // error: 'Settings' initializer is inaccessible due to 'private' protection level

Verwende Singletons sparsam. Ein Singleton ist ein globaler Zustand: Jeder Code kann ihn ändern, und Tests können nicht ohne Weiteres mit einer sauberen Kopie beginnen. Sie eignen sich für Dinge, die es wirklich nur einmal pro Programm gibt, etwa einen Logger oder die Konfiguration der App; ein Objekt als Parameter zu übergeben, ist oft klarer.

// klarer zum Testen: der Logger wird übergeben
func process(order: Order, log: Logger) { log.write("processing \(order.id)") }
challenge icon

Aufgabe

Einfach

Vervollständige den Singleton Logger. Er verfügt über eine static let shared-Instanz, einen privaten Initialisierer, ein private(set)-Array mit entries sowie log(_:level:), das [INFO] message oder [ERROR] message speichert (level ist info oder error). errorCount ist eine berechnete Eigenschaft. Der bereitgestellte Code ruft Logger.shared aus zwei verschiedenen Funktionen auf, daher müssen beide auf dieselbe Instanz zugreifen.

Der bereitgestellte Code liest Zeilen im Format info,message oder error,message, leitet Info-Zeilen über handleInfo und Fehlerzeilen über handleError weiter, gibt anschließend jeden Eintrag und errors: 1 aus.

Dein Code gehört in Logger.swift und Functions.swift. main.swift enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

// Gelieferter Ein-/Ausgabecode: belasse ihn wie er ist
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 iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Swift-Playground