一、观察者模式和发布订阅模式简介
1.1 观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
1.2 发布订阅模式
发布订阅模式理念和观察者模式相同,但是处理方式上不同:订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。
1.3 两者之间的差异
1、在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
2、发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
二、观察者模式和发布订阅模式优缺点
2.1 优点
共同有点:
1、都可以一对多
2、程序便于扩展
观察者模式:
单向解耦,发布者不需要清楚订阅者何时何地订阅,只需要维护订阅队列,发送消息即可
发布订阅模式:
双向解耦,发布者和订阅者都不用清楚对方,全部由订阅中心做处理
2.2 缺点
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
4、发布者不知道订阅者是否收到发布的消息。
5、订阅者不知道自己是否收到了发布者发出的所有消息。
6、发送者不能获知订阅者的执行情况。
7、没人知道订阅者何时开始收到消息。
8、发布订阅模式,中心任务过重,一旦崩溃,所有订阅者都会受到影响。
三、应用场景
1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
3、一个对象必须通知其他对象,而并不知道这些对象是谁。
4、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者/订阅发布创建一种链式触发机制。
四、实现
4.1 观察者模式
目标:
// 将观察者添加到数组中
- (void)addObserver:(NSObject *)obj selector:(SEL)aSelector {
NSDictionary *dict = @{@"object":obj,@"sel":NSStringFromSelector(aSelector)};
[self.observers addObject:dict];
}
//发送通知给观察者
- (void)notify {
if (_observers) {
for (NSDictionary *dict in _observers) {
if ([dict isKindOfClass:[NSDictionary class]]) {
NSObject *observer = dict[@"object"];
SEL aSelector = NSSelectorFromString(dict[@"sel"]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:aSelector];
#pragma clang diagnostic pop
}
}
}
}
//创建观察者数组
- (NSMutableArray *)observers {
if (!_observers) {
_observers = [NSMutableArray array];
}
return _observers;
}
观察者:
// 接收到消息通知
- (void)update {
NSLog(@"我观察的目标发生了变化,我接收到了新的信息,%@",self.name);
}
4.2 发布订阅模式
目标:
// 发送消息给通知中心
- (void)change {
[[NotifyCenter shared] notify:@"change"];
}
通知中心:
//创建通知中心单例
+ (NotifyCenter *)shared {
static NotifyCenter *shared;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[self alloc] init];
});
return shared;
}
// 添加观察者
- (void)addObserver:(NSObject *)object selector:(SEL)aSelector name:(NSString *)aName {
[self.set addObject:aName];
NSDictionary *dict = @{@"object":object,@"sel":NSStringFromSelector(aSelector)};
NSMutableDictionary *observer = [NSMutableDictionary dictionary];
observer[aName] = dict;
[self.observers addObject:observer];
}
// 通知观察者
- (void)notify:(NSString *)name {
if ([_set containsObject:name]) {
[_observers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *dict = obj[name];
if ([dict isKindOfClass:[NSDictionary class]]) {
NSObject *observer = dict[@"object"];
SEL aSelector = NSSelectorFromString(dict[@"sel"]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:aSelector];
#pragma clang diagnostic pop
}
}];
}
}
观察者:
// 接收到消息通知
- (void)update {
NSLog(@"我观察的目标发生了变化,我接收到了新的信息,%@",self.name);
}
4.4 调用
- (void)viewDidLoad {
[super viewDidLoad];
//观察者模式
//创建目标
self.subject = [[Subject alloc] init];
//创建观察者1
Observer *one = [[Observer alloc] init];
one.name = @"one";
//向目标订阅通知
[_subject addObserver:one selector:@selector(update)];
//创建观察者2
Observer *two = [[Observer alloc] init];
two.name = @"two";
//向目标订阅通知
[_subject addObserver:two selector:@selector(update)];
//发布订阅模式
//创建目标
self.subscribe = [[Subscribe alloc] init];
//创建观察者3
Observer *third = [[Observer alloc] init];
third.name = @"third";
//观察者向通知中心订阅消息
[[NotifyCenter shared] addObserver:third selector:@selector(update) name:@"change"];
//创建观察者4
Observer *four = [[Observer alloc] init];
four.name = @"four";
//观察者向通知中心订阅消息
[[NotifyCenter shared] addObserver:four selector:@selector(update) name:@"change"];
//按钮触发目标发送消息
UIButton *testBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, 100, 40)];
testBtn.backgroundColor = [UIColor lightGrayColor];
[testBtn addTarget:self action:@selector(testButton) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:testBtn];
}
- (void)testButton {
NSLog(@"差不多11:45分了,到点吃饭了");
NSLog(@"观察者模式发送消息");
[_subject notify];
NSLog(@"发布-订阅模式发送消息");
[_subscribe change];
}
五、总结
1、虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。
2、两种模式都可以用于松散耦合,改进代码管理和潜在的复用。