Menu
Coddy logo textTech

Шаблон Singleton

Часть раздела Объектно-ориентированное программирование путешествия по Swift на Coddy. Урок 42 из 57.

Шаблон проектирования — это проверенное решение распространённой задачи проектирования. Шаблон одиночка гарантирует, что у класса будет ровно один экземпляр, доступный откуда угодно. В Swift это static let, содержащий экземпляр, а также private init, чтобы никто не мог создать другой:

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

    private init() {}
}

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

Вывод:

dark

Каждое использование Settings.shared ссылается на один и тот же объект, поэтому изменение, внесённое в одной части программы, видно повсюду. static let создаётся лениво при первом использовании и безопасно даже в случае, когда несколько потоков запускаются одновременно:

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)

Вывод:

3
true

Поскольку инициализатор является закрытым, Settings() не компилируется за пределами класса, что сохраняет обещание «только один»:

// let other = Settings()   // error: инициализатор 'Settings' недоступен из-за уровня защиты 'private'

Используйте синглтоны экономно. Синглтон — это глобальное состояние: любой код может его изменить, и тесты не могут легко начать работу с чистой копией. Они подходят для сущностей, которые действительно существуют в единственном экземпляре на программу, например для логгера или конфигурации приложения; часто понятнее передавать объект в качестве параметра.

// понятнее для тестирования: логгер передаётся
func process(order: Order, log: Logger) { log.write("processing \(order.id)") }
challenge icon

Задание

Легко

Завершите реализацию синглтона Logger. В нём есть экземпляр static let shared, приватный инициализатор, массив entries с модификатором доступа private(set) и метод log(_:level:), который сохраняет [INFO] message или [ERROR] message (уровень — info или error). errorCount — вычисляемое свойство. Предоставленный код вызывает Logger.shared из двух разных функций, поэтому обе должны обращаться к одному и тому же экземпляру.

Предоставленный код считывает строки info,message или error,message, передаёт строки с информацией через handleInfo, а строки с ошибками — через handleError, затем выводит каждую запись и errors: 1.

Ваш код должен находиться в Logger.swift и Functions.swift. В main.swift находится предоставленный код ввода/вывода, и его нельзя редактировать.

Попробуйте сами

// Предоставленный код ввода/вывода: оставьте его как есть
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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Swift