iOS 設(shè)計(jì)模式之二十(觀察者模式)

一、概念

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辑莫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子罩引,更是在濱河造成了極大的恐慌各吨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袁铐,死亡現(xiàn)場(chǎng)離奇詭異揭蜒,居然都是意外死亡横浑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門屉更,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)徙融,“玉大人,你說(shuō)我怎么就攤上這事瑰谜∑奂剑” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵萨脑,是天一觀的道長(zhǎng)隐轩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)渤早,這世上最難降的妖魔是什么职车? 我笑而不...
    開(kāi)封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蛛芥,結(jié)果婚禮上提鸟,老公的妹妹穿的比我還像新娘。我一直安慰自己仅淑,他們只是感情好称勋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著涯竟,像睡著了一般赡鲜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庐船,一...
    開(kāi)封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天银酬,我揣著相機(jī)與錄音,去河邊找鬼筐钟。 笑死揩瞪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的篓冲。 我是一名探鬼主播李破,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼壹将!你這毒婦竟也來(lái)了嗤攻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诽俯,失蹤者是張志新(化名)和其女友劉穎妇菱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闯团,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年辛臊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷俭。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浪讳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涌萤,到底是詐尸還是另有隱情淹遵,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布负溪,位于F島的核電站透揣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏川抡。R本人自食惡果不足惜辐真,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崖堤。 院中可真熱鬧侍咱,春花似錦、人聲如沸密幔。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)胯甩。三九已至昧廷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間偎箫,已是汗流浹背木柬。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淹办,地道東北人眉枕。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像怜森,于是被迫代替她去往敵國(guó)和親齐遵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容