本文大部分內(nèi)容翻譯至《Pro Design Pattern In Swift》By Adam Freeman,一些地方做了些許修改,并將代碼升級到了Swift2.0,翻譯不當(dāng)之處望多包涵。
觀察者模式(The Observer Pattern)
觀察者設(shè)計(jì)模式定義了對象間的一種一對多的依賴關(guān)系垒酬,以便一個(gè)對象的狀態(tài)發(fā)生變化時(shí)刻蚯,所有依賴于它的對象都得到通知并自動刷新。
示例工程
OS X Command Line Tool工程:
SystemComponents.swift
class ActivityLog {
func logActivity(activity:String) {
print("Log: \\(activity)")
}
}
class FileCache {
func loadFiles(user:String) {
print("Load files for \\(user)")
}
}
class AttackMonitor {
var monitorSuspiciousActivity: Bool = false {
didSet {
print("Monitoring for attack: \\(monitorSuspiciousActivity)")
}
}
}
ActivityLog類代表系統(tǒng)的事件日志輸出必尼;FileCache類代表一個(gè)給定的用戶的文件加載;AttackMonitor類代表可疑事件發(fā)生時(shí)的安全服務(wù)篡撵。
Authentication.swift
class AuthenticationManager {
private let log = ActivityLog()
private let cache = FileCache()
private let monitor = AttackMonitor()
func authenticate(user:String, pass:String) -> Bool {
var result = false
if (user == "bob" && pass == "secret") {
result = true
print("User \\(user) is authenticated")
// call system components
log.logActivity("Authenticated \\(user)")
cache.loadFiles(user)
monitor.monitorSuspiciousActivity = false
} else {
print("Failed authentication attempt")
// call system components
log.logActivity("Failed authentication: \\(user)")
monitor.monitorSuspiciousActivity = true
}
return result
}
}
AuthenticationManager類代表用密碼來認(rèn)證用戶的服務(wù)類判莉。可以看出認(rèn)證成功后會輸出成功日志并加載用戶的文件育谬,失敗后會輸出失敗日志并輸出受攻擊的警告券盅。
main.swift
let authM = AuthenticationManager()
authM.authenticate("bob", pass: "secret")
print("--------------")
authM.authenticate("joe", pass: "shhh")
運(yùn)行程序:
User bob is authenticated
Log: Authenticated bob
Load files for bob
Monitoring for attack: false
--------------
Failed authentication attempt
Log: Failed authentication: joe
Monitoring for attack: true
理解觀察者模式解決的問題
示例中的代碼結(jié)構(gòu)在真正的項(xiàng)目中十分常見,一個(gè)事件的發(fā)生引起了一系列的其它事件的發(fā)生膛檀。
問題發(fā)生在操作初始事件的類里(本例中就是AuthenticationManager類)渗饮,它必須知道觸發(fā)的其它事件的詳細(xì)和它們是如何操作的。如果其中的一個(gè)觸發(fā)類做一些修改宿刮,那么相應(yīng)的初始事件類中也要做相應(yīng)的修改互站。
理解觀察者模式
觀察者模式通過將它們分成被觀察者和觀察者來改變這種關(guān)系。被觀察者持有觀察者的集合僵缺,當(dāng)發(fā)生改變時(shí)就通知它們胡桃。
實(shí)現(xiàn)觀察者模式
實(shí)現(xiàn)觀察者模式的關(guān)鍵是用協(xié)議定義觀察者和被觀察者的協(xié)議。
Observer.swift
protocol Observer : class {
func notify(user:String, success:Bool)
}
protocol Subject {
func addObservers(observers:Observer...)
func removeObserver(observer:Observer)
}
注意到Observer協(xié)議的class關(guān)鍵字磕潮, 這樣做的原因是我們后面要進(jìn)行對象的比較翠胰。
創(chuàng)建被觀察者基類
我們知道被觀察類持有觀察者類的集合,所以同時(shí)需要GCD并發(fā)保護(hù)自脯。
Observer.swift
import Foundation
protocol Observer : class {
func notify(user:String, success:Bool)
}
protocol Subject {
func addObservers(observers:Observer...)
func removeObserver(observer:Observer)
}
class SubjectBase : Subject {
private var observers = [Observer]()
private var collectionQueue = dispatch_queue_create("colQ",DISPATCH_QUEUE_CONCURRENT)
func addObservers(observers: Observer...) {
dispatch_barrier_sync(self.collectionQueue){[weak self] in
for newOb in observers {
self!.observers.append(newOb)
}
}
}
func removeObserver(observer: Observer) {
dispatch_barrier_sync(self.collectionQueue){[weak self] in
self!.observers = self!.observers.filter(){
$0 !== observer
}
}
}
func sendNotification(user:String, success:Bool) {
dispatch_sync(self.collectionQueue){ [weak self] in
for ob in self!.observers {
ob.notify(user, success: success)
}
}
}
}
實(shí)現(xiàn)被觀察者協(xié)議
Authentication.swift
class AuthenticationManager : SubjectBase {
func authenticate(user:String, pass:String) -> Bool {
var result = false
if (user == "bob" && pass == "secret") {
result = true
print("User \\(user) is authenticated")
} else {
print("Failed authentication attempt")
}
sendNotification(user, success: result)
return result
}
}
實(shí)現(xiàn)觀察者協(xié)議
SystemComponents.swift
class ActivityLog : Observer {
func notify(user: String, success: Bool) {
print("Auth request for \\(user). Success: \\(success)")
}
func logActivity(activity:String) {
print("Log: \\(activity)")
}
}
class FileCache : Observer {
func notify(user: String, success: Bool) {
if (success) {
loadFiles(user)
}
}
func loadFiles(user:String) {
print("Load files for \\(user)")
}
}
class AttackMonitor : Observer {
func notify(user: String, success: Bool) {
monitorSuspiciousActivity = !success
}
var monitorSuspiciousActivity: Bool = false {
didSet {
print("Monitoring for attack: \\(monitorSuspiciousActivity)")
}
}
}
最后我們再看main.swift:
let log = ActivityLog()
let cache = FileCache()
let monitor = AttackMonitor()
let authM = AuthenticationManager()
authM.addObservers(log, cache, monitor)
authM.authenticate("bob", pass: "secret")
print("-----")
authM.authenticate("joe", pass: "shhh")
運(yùn)行程序:
User bob is authenticated
Auth request for bob. Success: true
Load files for bob
Monitoring for attack: false
-----
Failed authentication attempt
Auth request for joe. Success: false
Monitoring for attack: true
我們再添加觀察者就顯得很容易了之景,只要實(shí)現(xiàn)觀察者協(xié)議然后調(diào)用addOberservers方法添加觀察者就行了。
觀察者模式的變形
泛化通知類型
示例中的notify 方法只能接收認(rèn)證的通知膏潮,這其實(shí)是很糟糕的一種設(shè)計(jì)锻狗。
...
func notify(user:String, success:Bool)
...
下面我們做一些修改,使得它所接受的數(shù)據(jù)類型和通知類型都可以多樣化。
Observer.swift
import Foundation
enum NotificationTypes : String {
case AUTH_SUCCESS = "AUTH_SUCCESS"
case AUTH_FAIL = "AUTH_FAIL"
}
struct Notification {
let type:NotificationTypes
let data:Any?
}
protocol Observer : class {
func notify(notification:Notification)
}
......
func sendNotification(notification:Notification) {
dispatch_sync(self.collectionQueue){ [weak self] in
for ob in self!.observers {
ob.notify(notification)
}
}
}
.......
接著我們修改SystemComponents.swift :
class ActivityLog : Observer {
func notify(notification:Notification) {
print("Auth request for \\(notification.type.rawValue) " + "Success: \\(notification.data!)")
}
func logActivity(activity:String) {
print("Log: \\(activity)")
}
}
class FileCache : Observer {
func notify(notification:Notification) {
if (notification.type == NotificationTypes.AUTH_SUCCESS) {
loadFiles(notification.data! as! String)
}
}
func loadFiles(user:String) {
print("Load files for \\(user)");
}
}
class AttackMonitor : Observer {
func notify(notification: Notification) {
monitorSuspiciousActivity = (notification.type == NotificationTypes.AUTH_FAIL)
}
var monitorSuspiciousActivity: Bool = false {
didSet {
print("Monitoring for attack: \\(monitorSuspiciousActivity)");
}
}
}
最后是AuthenticationManager.swift:
class AuthenticationManager : SubjectBase {
func authenticate(user:String, pass:String) -> Bool {
var nType = NotificationTypes.AUTH_FAIL
if (user == "bob" && pass == "secret") {
nType = NotificationTypes.AUTH_SUCCESS
print("User \\(user) is authenticated")
} else {
print("Failed authentication attempt")
}
sendNotification(Notification(type: nType, data: user))
return nType == NotificationTypes.AUTH_SUCCESS
}
}
運(yùn)行程序:
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #ffffff}span.s1 {font-variant-ligatures: no-common-ligatures}
User bob is authenticated
Auth request for AUTH_SUCCESS Success: bob
Load files for bob
Monitoring for attack: false
-----
Failed authentication attempt
Auth request for AUTH_FAIL Success: joe
Monitoring for attack: true