Menu
Coddy logo textTech

Patrón Singleton

Parte de la sección Programación orientada a objetos del Journey de Swift de Coddy. Lección 42 de 57.

Un patrón de diseño es una solución probada para un problema de diseño común. El patrón singleton garantiza que una clase tenga exactamente una instancia, accesible desde cualquier lugar. En Swift, es un static let que contiene la instancia, además de un private init para que nadie pueda crear otra:

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

    private init() {}
}

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

Salida:

dark

Cada uso de Settings.shared hace referencia al mismo objeto, por lo que un cambio realizado en una parte del programa es visible en todas partes. static let se crea de forma diferida en el primer uso y de manera segura incluso cuando varios hilos se inician al mismo tiempo:

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)

Salida:

3
true

Como el inicializador es privado, Settings() no se compila fuera de la clase, lo que mantiene la promesa de «solo uno»:

// let other = Settings()   // error: el inicializador de 'Settings' es inaccesible debido al nivel de protección 'private'

Usa los singleton con moderación. Un singleton es un estado global: cualquier código puede cambiarlo y las pruebas no pueden empezar fácilmente con una copia limpia. Son adecuados para elementos que realmente deben existir una sola vez por programa, como un registrador o la configuración de la aplicación; pasar un objeto como parámetro suele ser más claro.

// más claro para probar: el logger se pasa
func process(order: Order, log: Logger) { log.write("processing \(order.id)") }
challenge icon

Desafío

Fácil

Completa el singleton Logger. Tiene una instancia static let shared, un inicializador privado, un array private(set) de entries y log(_:level:), que almacena [INFO] message o [ERROR] message (el nivel es info o error). errorCount es una propiedad calculada. El código proporcionado llama a Logger.shared desde dos funciones diferentes, por lo que ambas deben acceder a la misma instancia.

El código proporcionado lee líneas info,message o error,message, envía las líneas de información mediante handleInfo y las líneas de error mediante handleError, después imprime cada entrada y errors: 1.

Tu código debe ir en Logger.swift y Functions.swift. main.swift contiene el código de entrada/salida proporcionado y no se puede editar.

Pruébalo tú mismo

// Código de entrada/salida suministrado: déjalo como está
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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Playground de Swift