张东轩的博客

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。

0%

iOS 观察者模式

引言

差不多有半年没有写过博客了,知识沉淀的感觉让我十分怀念。最近在工作中用到的观察者模式比较多,发现客户端开发中无论是Android还是iOS都需要很多经常的利用到这种模式。这种模式会在很大程度上达到层次解耦合的作用,具体如果解耦会在下文中提到。

模式定义

定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。 – 刘伟老师

模式概述

观察者模式是使用频率很高的模式,它定义了对象与对象之间的依赖关系,当一个对象的状态发生变化的时候自动地去通知其他对象响应此变化。发生改变的对象被称为观察目标(Subject),被通知去响应变化的被称为观察者(Observer),可以动态的去增加和删除观察者,从而做到观察者和被观察者之家的解耦,使系统更容易拓展。

举个例子

“铃响,学生上课;铃再响,学生下课”。在这个场景中铃声就是同学们的观察目标,而学生就是观察者。

模式类图

  • Subjec: 目标,即被观察者。其中会定义一个观察者集合,用来存放任意数量的观察者,并提供管理这些观察者的方法。同时定义了通知的方法notify()。这个类可以是接口,抽象类或者具体类;
  • ConcreteSubject: 具体的目标类。当其状态改变时,会向各个观察者发出通知,此类可以根据情况来决定是否对目标类进行扩展;
  • Observer: 观察者类对观察目标的改变做出响应。一般定义为接口,该接口声明了更新数据的方法update();
  • ConcreteObserver: 具体观察者,它是真正响应变化的类,在一般情况下它维护一个指向具体目标对象的引用,它具体存储观察者的有关状态(例如下课的铃响,学生记住下课的状态),这些状态要和具体目标的状态保持一致。它实现了在Observer接口中的update()方法。可以调用观察目标对象的addObserver()和removeObserver()将自己添加到观察者队列或者从观察者队列中删除。
  • ConcreteObserver: 具体观察者

##注意
在OC中没有接口这一说,并且其与接口类似的协议也是不能声明对象的!!

代码示例

下面是该模式在Objective-C中的实现。

Observer.h

1
2
3
4
5
@interface Observer : NSObject

-(void)update;

@end

Observer.m

1
2
3
4
5
6
7
@implementation Observer

-(void)update{

}

@end

ConcreteObserver.h

1
2
3
4
5
6
7
#import "Observer.h"
@class Subject;

@interface ConcreteObserver : Observer

@property(nonatomic,weak)Subject *subject;
@end

ConcreteObserver.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import "ConcreteObserver.h"
#import "Subject.h"

@implementation ConcreteObserver{
CLASS_STATE state;
}

-(void)update{
state = [_subject state];
if(state){
NSLog(@"学生冲进教室");
}else{
NSLog(@"学生冲出教室");
}
}

@end

Subject.h

1
2
3
4
5
6
7
8
9
10
11
12
13
@class Observer;

typedef NS_ENUM(NSInteger, CLASS_STATE) {
CLASS_OFF = 0, //下课
CLASS_ON, //上课
};

@interface Subject : NSObject
@property(nonatomic,assign)SUBJECT_STATE state;
-(BOOL)addObserver:(Observer*)observer;
-(BOOL)removeObserver:(Observer*)observer;
-(void)notify;
@end

Subject.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#import "Subject.h"
#import "Observer.h"

@implementation Subject{
NSMutableArray *observerArray;
}

-(instancetype)init{
if (self = [super init]) {
observerArray = [[NSMutableArray alloc]init];
}
return self;
}

-(BOOL)addObserver:(Observer*)observer{
if (![observerArray containsObject:observer]) {
[observerArray addObject:observer];
return YES;
}
return NO;
}

-(BOOL)removeObserver:(Observer*)observer{
if ([observerArray containsObject:observer]) {
[observerArray removeObject:observer];
return YES;
}
return NO;
}

-(void)notify{
for (Observer* observer in observerArray) {
[observer update];
}
}

-(void)setState:(CLASS_STATE)state{
_state = state;
if (_state) {
NSLog(@"上课铃响");
}else{
NSLog(@"下课铃响");
}
}
@end

另外为了尽量匹配观察者模式的类图结构,还有一个Subject的子类ConcreteSubject,其实在这个例子中这个类并不需要存在。
ConcreteSubject.h

1
2
3
4
#import "Subject.h"
@interface ConcreteSubject : Subject

@end

ConcreteSubject.m

1
2
3
4
#import "ConcreteSubject.h"
@implementation ConcreteSubject

@end

最后在main函数中我们运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Subject * subject = [[Subject alloc]init];

ConcreteObserver *observer1 = [[ConcreteObserver alloc]init];
ConcreteObserver *observer2= [[ConcreteObserver alloc]init];
ConcreteObserver *observer3 = [[ConcreteObserver alloc]init];
ConcreteObserver *observer4 = [[ConcreteObserver alloc]init];

observer1.subject = subject;
observer2.subject = subject;
observer3.subject = subject;
observer4.subject = subject;

[subject addObserver:observer1];
[subject addObserver:observer2];
[subject addObserver:observer3];
[subject addObserver:observer4];

[subject setState:CLASS_ON];
[subject notify];

[subject setState:CLASS_OFF];
[subject notify];

运动结果:


思考

以上是比较复杂的观察着模式,在更新观察者的状态的时候需要获被观察者的状态。但是在很多情况下,我们并不需获取被观察者的状态,也就不需要持有被观察者的对象,这样会大大的简化模式模型。
比如在网络层和业务层之间,网络层下载数据之后如何通知业务层进行更新呢?如果像以上这种做法很显然观察者和被观察者之间相互依赖,层次之间的耦合度还是比较高的,所以代码应该如何组织呢?