一、概念
1椅野、觀察者模式的動(dòng)機(jī)
? “Hands up!”在美國(guó)大片中凡资,當(dāng)美軍小隊(duì)包圍一個(gè)恐怖分子時(shí),如果恐怖分子有把手伸進(jìn)口袋或者衣服里的動(dòng)作冗懦,那么美軍士兵就會(huì)果斷開(kāi)槍射擊爽冕。這個(gè)時(shí)候恐怖分子就是觀察目標(biāo),美軍士兵就是觀察者批狐,隨著觀察目標(biāo)的動(dòng)作美軍士兵做出不同的反應(yīng)扇售。
? 在軟件系統(tǒng)中也存在類似的情況,一個(gè)對(duì)象的狀態(tài)或行為的變化將導(dǎo)致其他對(duì)象的狀態(tài)或行為也發(fā)生改變嚣艇,它們之間將產(chǎn)生聯(lián)動(dòng)承冰,為了更好地描述對(duì)象之間存在的這種一對(duì)多(包括一對(duì)一)的聯(lián)動(dòng),觀察者模式應(yīng)運(yùn)而生食零。
2困乒、觀察者模式的定義
? 觀察者模式(Observer Pattern):定義對(duì)象之間的一種一對(duì)多依賴關(guān)系,使得每當(dāng)一個(gè)對(duì)象狀態(tài)發(fā)生改變時(shí)贰谣,其相關(guān)依賴對(duì)象皆得到通知并被自動(dòng)更新娜搂。觀察者模式的別名包括發(fā)布-訂閱(Publish/Subscribe)模式迁霎、模型-視圖(Model/View)模式、源-監(jiān)聽(tīng)器(Source/Listener)模式或從屬者(Dependents)模式百宇。觀察者模式是一種對(duì)象行為型模式考廉。
3、觀察者模式的4個(gè)角色
1)Subject(目標(biāo)):目標(biāo)又稱為主題携御,它是指被觀察的對(duì)象昌粤。在目標(biāo)中定義了一個(gè)觀察者集合,一個(gè)觀察目標(biāo)可以接受任意數(shù)量的觀察者來(lái)觀察啄刹,它提供一系列方法來(lái)增加和刪除觀察者對(duì)象涮坐,同時(shí)它定義了通知方法notify()。目標(biāo)類可以是接口誓军,也可以是抽象類或具體類袱讹。
2)ConcreteSubject(具體目標(biāo)):具體目標(biāo)是目標(biāo)類的子類,通常它包含有經(jīng)常發(fā)生改變的數(shù)據(jù)昵时,當(dāng)它的狀態(tài)發(fā)生改變時(shí)捷雕,向它的各個(gè)觀察者發(fā)出通知;同時(shí)它還實(shí)現(xiàn)了在目標(biāo)類中定義的抽象業(yè)務(wù)邏輯方法(如果有的話)壹甥。如果無(wú)須擴(kuò)展目標(biāo)類非区,則具體目標(biāo)類可以省略。
3)Observer(觀察者):觀察者將對(duì)觀察目標(biāo)的改變做出反應(yīng)盹廷,觀察者一般定義為接口,該接口聲明了更新數(shù)據(jù)的方法update()久橙,因此又稱為抽象觀察者俄占。
4)ConcreteObserver(具體觀察者):在具體觀察者中維護(hù)一個(gè)指向具體目標(biāo)對(duì)象的引用,它存儲(chǔ)具體觀察者的有關(guān)狀態(tài)淆衷,這些狀態(tài)需要和具體目標(biāo)的狀態(tài)保持一致缸榄;它實(shí)現(xiàn)了在抽象觀察者Observer中定義的update()方法。通常在實(shí)現(xiàn)時(shí)祝拯,可以調(diào)用具體目標(biāo)類的attach()方法將自己添加到目標(biāo)類的集合中或通過(guò)detach()方法將自己從目標(biāo)類的集合中刪除甚带。
觀察者模式包含觀察目標(biāo)和觀察者兩類對(duì)象,一個(gè)目標(biāo)可以有任意數(shù)目的與之相依賴的觀察者佳头,一旦觀察目標(biāo)的狀態(tài)發(fā)生改變鹰贵,所有的觀察者都將得到通知。作為對(duì)這個(gè)通知的響應(yīng)康嘉,每個(gè)觀察者都將監(jiān)視觀察目標(biāo)的狀態(tài)以使其狀態(tài)與目標(biāo)狀態(tài)同步碉输,這種交互也稱為發(fā)布-訂閱(Publish-Subscribe)。觀察目標(biāo)是通知的發(fā)布者亭珍,它發(fā)出通知時(shí)并不需要知道誰(shuí)是它的觀察者敷钾,可以有任意數(shù)目的觀察者訂閱它并接收通知枝哄。
4、結(jié)構(gòu)圖
二阻荒、示例
1)首先定義一個(gè)Observer協(xié)議挠锥,有一個(gè)updateWithSubject()方法,表示抽象觀察者侨赡;
2)然后定義一個(gè)Soldier類蓖租,遵循Observer協(xié)議,實(shí)現(xiàn)協(xié)議的方法辆毡,表示具體觀察者菜秦;
3)然后定義一個(gè)Subject類,有添加觀察者舶掖、刪除觀察者和通知等方法球昨,表示目標(biāo)類;
4)最后定義一個(gè)Terrorist類眨攘,有一個(gè)hp血量的屬性主慰,實(shí)現(xiàn)了通知方法,表示具體目標(biāo)類鲫售。
Observer協(xié)議:
@class Subject;
// 抽象觀察者共螺,接口
@protocol Observer <NSObject>
- (void)updateWithSubject:(Subject *)subject; //聲明響應(yīng)方法
@end
typedef id<Observer> Observer;
Soldier類:
@class Terrorist;
// 士兵類:具體觀察者
@interface Soldier : NSObject<Observer>
@property(nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
@implementation Soldier
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)updateWithSubject:(Subject *)subject {
Terrorist *terrorist = (Terrorist *)subject;
NSLog(@"%@反擊, %@的hp = %ld", self.name, terrorist.name, terrorist.hp);
}
@end
Subject類:
// 抽象目標(biāo)類
@interface Subject : NSObject
- (void)attach:(Observer)observer; //注冊(cè)方法,用于向觀察者集合中增加一個(gè)觀察者
- (void)detach:(Observer)observer; //注銷方法情竹,用于在觀察者集合中刪除一個(gè)觀察者
- (void)notify; //聲明抽象通知方法
- (NSArray *)getObserverList;
@end
@interface Subject ()
//定義一個(gè)觀察者集合用于存儲(chǔ)所有觀察者對(duì)象
@property(nonatomic, strong) NSMutableArray *observerList;
@end
@implementation Subject
- (instancetype)init
{
self = [super init];
if (self) {
_observerList = [NSMutableArray array];
}
return self;
}
- (void)attach:(Observer)observer {
[self.observerList addObject:observer];
}
- (void)detach:(Observer)observer {
[self.observerList removeObject:observer];
}
- (void)notify {}
- (NSArray *)getObserverList {
return [self.observerList copy];
}
@end
Terrorist類:
// 恐怖分子:具體目標(biāo)類
@interface Terrorist : Subject
@property(nonatomic, assign) NSInteger hp; //生命值
@property(nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
@implementation Terrorist
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
_hp = 100;
}
return self;
}
- (void)notify {
NSLog(@"%@開(kāi)始攻擊了, hp = %ld", self.name, self.hp);
// 遍歷觀察者集合藐不,調(diào)用每一個(gè)觀察者的響應(yīng)方法
for (Observer observer in self.observerList) {
self.hp -= 25;
[observer updateWithSubject:self];
}
}
@end
運(yùn)行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
// 定義觀察目標(biāo)對(duì)象
Subject *Thanos = [[Terrorist alloc] initWithName:@"滅霸"];
// 定義四個(gè)觀察者對(duì)象
Observer IronMan = [[Soldier alloc] initWithName:@"鋼鐵俠"];
Observer CaptainAmerica = [[Soldier alloc] initWithName:@"美國(guó)隊(duì)長(zhǎng)"];
Observer BlackWidow = [[Soldier alloc] initWithName:@"黑寡婦"];
Observer Hulk = [[Soldier alloc] initWithName:@"綠巨人"];
// 添加觀察者
[Thanos attach:IronMan];
[Thanos attach:CaptainAmerica];
[Thanos attach:BlackWidow];
[Thanos attach:Hulk];
// 通知大家
[Thanos notify];
}
打印結(jié)果:
滅霸開(kāi)始攻擊了, hp = 100
鋼鐵俠反擊, 滅霸的hp = 75
美國(guó)隊(duì)長(zhǎng)反擊, 滅霸的hp = 50
黑寡婦反擊, 滅霸的hp = 25
綠巨人反擊, 滅霸的hp = 0
三、總結(jié)
? 觀察者模式是一種使用頻率非常高的設(shè)計(jì)模式秦效,無(wú)論是移動(dòng)應(yīng)用雏蛮、Web應(yīng)用或者桌面應(yīng)用,觀察者模式幾乎無(wú)處不在阱州,它為實(shí)現(xiàn)對(duì)象之間的聯(lián)動(dòng)提供了一套完整的解決方案挑秉,凡是涉及到一對(duì)一或者一對(duì)多的對(duì)象交互場(chǎng)景都可以使用觀察者模式。
1苔货、優(yōu)點(diǎn)
1犀概、觀察者模式可以實(shí)現(xiàn)表示層和數(shù)據(jù)邏輯層的分離,定義了穩(wěn)定的消息更新傳遞機(jī)制夜惭,并抽象了更新接口姻灶,使得可以有各種各樣不同的表示層充當(dāng)具體觀察者角色。
2滥嘴、觀察者模式在觀察目標(biāo)和觀察者之間建立一個(gè)抽象的耦合木蹬。觀察目標(biāo)只需要維持一個(gè)抽象觀察者的集合,無(wú)須了解其具體觀察者。由于觀察目標(biāo)和觀察者沒(méi)有緊密地耦合在一起镊叁,因此它們可以屬于不同的抽象化層次尘颓。
3、觀察者模式支持廣播通信晦譬,觀察目標(biāo)會(huì)向所有已注冊(cè)的觀察者對(duì)象發(fā)送通知疤苹,簡(jiǎn)化了一對(duì)多系統(tǒng)設(shè)計(jì)的難度。
4敛腌、觀察者模式滿足“開(kāi)閉原則”的要求卧土,增加新的具體觀察者無(wú)須修改原有系統(tǒng)代碼,在具體觀察者與觀察目標(biāo)之間不存在關(guān)聯(lián)關(guān)系的情況下像樊,增加新的觀察目標(biāo)也很方便尤莺。
2、缺點(diǎn)
1生棍、如果一個(gè)觀察目標(biāo)對(duì)象有很多直接和間接觀察者颤霎,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。
2涂滴、如果在觀察者和觀察目標(biāo)之間存在循環(huán)依賴友酱,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰柔纵。
3缔杉、觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化搁料。
3或详、適用場(chǎng)景
? 一個(gè)對(duì)象的改變將導(dǎo)致一個(gè)或多個(gè)其他對(duì)象也發(fā)生改變,而并不知道具體有多少對(duì)象將發(fā)生改變郭计,也不知道這些對(duì)象是誰(shuí)鸭叙。
4、iOS應(yīng)用舉例
? iOS中的KVO拣宏、NSNotication都是觀察者模式。其中杠人,NSNotificationCenter和NSNotification實(shí)現(xiàn)了一對(duì)多的發(fā)布-訂閱模型勋乾。KVO是被觀察的對(duì)象直接向觀察者發(fā)出通知,一般用于綁定特定對(duì)象屬性的值嗡善。
Demo地址:iOS-Design-Patterns