单例模式
本课属于 Coddy Swift 学习旅程的“面向对象编程”单元。第 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() // 错误:'Settings' 初始化器受 'private' 访问级别限制,无法访问少用单例。单例属于全局状态:任何代码都可以修改它,而且测试很难从干净的副本开始。单例适用于真正只需在每个程序中存在一个的事物,例如日志记录器或应用的配置;将对象作为参数传入通常更清晰。
// 更容易测试:日志记录器作为参数传入。
func process(order: Order, log: Logger) { log.write("processing \(order.id)") }挑战
简单补全单例 Logger。它有一个 static let shared 实例、一个私有初始化器、一个 private(set) 的 entries 数组,以及 log(_:level:) 方法,该方法会存储 [INFO] message 或 [ERROR] message(level 为 info 或 error)。errorCount 是一个计算属性。提供的代码会从两个不同的函数中调用 Logger.shared,因此这两个函数都必须访问同一个实例。
提供的代码会读取 info,message 或 error,message 这样的行,通过 handleInfo 处理 info 行,通过 handleError 处理 error 行,然后输出每条记录和 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)")
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Swift 在线编辑器