观察者模式

组件间通信,我会优先使用路由的通信方式(iOS中需要用到 runtime,Android 中需要用到反射和注解),组件之间按照路由协议(类似 url)实现各自的职责即可,组件间的松耦合性增强了软件设计的弹性和高可用性。

路由结合观察者模式,可以让你的组件设计更上一层楼。路由可以解决单向调用的问题,让组件之间无需知道对方是否存在。观察者模式可以让调用者(使用你框架的)可以很方便的知道组件的内部事件。

武林至尊,宝刀屠龙,号令天下,莫敢不从,倚天不出,谁与争锋?

在说观察者模式之前,先介绍一下 iOS 中的代理(delegate).

代理 delegate

在 iOS 中,代理(delegate)的本质是 protocol,类似 java 中的 Interface,一般用来处理 一对一 的关系,如下图所示:

下面的例子模拟了调用和实现过程,使用 Bank 对象和 BankDelegate 代理来模拟这种模式。Bank 有变动的时候,通过 notifyAccount 来授权 onAccountChanged 通知用户。

BankDelegate.h

1
2
3
4
5
@protocol BankDelegate <NSObject>
- (void)onAccountChanged:(NSUInteger)account;
@end

Bank.h

1
2
3
4
5
6
7
@interface Bank : NSObject
@property (nonatomic, weak) id<BankDelegate> delegate;
- (void)notifyAccount;
@end

Bank.m

1
2
3
4
5
6
7
8
9
10
@implementation Bank
- (void)notifyAccount
{
if ([self.delegate respondsToSelector:@selector(onAccountChanged:)]) {
[self.delegate onAccountChanged:100];
}
}
@end

使用者实现 delegate

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)viewDidLoad
{
[super viewDidLoad];
Bank *bank = [Bank new];
bank.delegate = self;
[bank notifyAccount];
}
- (void)onAccountChanged:(NSUInteger)account
{
NSLog(@"Bank tell me that account is changed. %zd", account);
}

当然,delegate 也可以做到 一对多,改造一下 Bank 就可以实现。

1
2
3
4
5
6
7
@interface Bank : NSObject
- (void)addBankDelegate:(id<BankDelegate>)delegate;
- (void)notifyAccount;
@end

Bank 内部使用数组将 addBankDelegate 得到的 delegate 存起来,notifyAccount 中就可以进行通知了。

1
2
3
4
5
Bank *bank = [Bank new];
// 模拟添加多个 delegate
[bank addBankDelegate:self];
[bank addBankDelegate:self];
[bank notifyAccount];

观察者模式

理解了 delegate,观察者模式就很好理解了。

当一个对象改变状态时,它的所有依赖着都会收到通知并自动更新,这是观察者模式的常规定义。

观察者模式是一种 一对多 的设计模式,如下图所示:

继续上面的例子,使用 Bank 对象和 BankDelegate 来模拟这种模式,Bank 有变动的时候,通过 notifyAccount 来授权 onAccountChanged 通知所有注册了 BankDelegate 的用户。

Talk is cheap. Show me the code

BankDelegate.h

1
2
3
4
5
@protocol BankDelegate <NSObject>
- (void)onAccountChanged:(NSUInteger)account;
@end

Bank.h

1
2
3
4
5
6
7
8
9
@interface Bank : NSObject
- (void)addBankObserver:(id<BankDelegate>)delegate;
- (void)removeBankObserver:(id<BankDelegate>)delegate;
- (void)notifyAccount;
@end

Bank.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
- (void)notifyAccount
{
for (id<BankDelegate> delegate in self.delegates) {
if ([delegate respondsToSelector:@selector(onAccountChanged:)]) {
[delegate onAccountChanged:100];
}
}
}
- (void)addBankObserver:(id<BankDelegate>)delegate
{
if (nil == delegate) {
return;
}
[self.delegates addObject:delegate];
}
- (void)removeBankObserver:(id<BankDelegate>)delegate
{
if (self.delegates.count > 0) {
[self.delegates removeObject:delegate];
}
}

调用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)viewDidLoad
{
[super viewDidLoad];
[self registerObserver];
}
- (void)onAccountChanged:(NSUInteger)account
{
NSLog(@"Bank tell me that account is changed. %zd", account);
}
- (void)registerObserver
{
Bank *bank = [Bank new];
[bank addBankObserver:self];
[bank addBankObserver:self];
[bank notifyAccount];
}

看到这里,你应该发现 delegate 的设计其实就是观察者的一种设计手段而已,它本身也是观察者模式。

在 iOS 中,除了 delegate,还有很多这种设计模式的体现,如 KVO、Notification、Observer、Block 等。

发布-订阅模式

观察者模式中观察者对被观察者(Bank)是有感知的,至少需要实现对应的 BankDelegate,二者之间还是是有一定的耦合度。

那么,有没有一种方法再来降低这种耦合,让双方都不用去关心对方的存在呢?发布-订阅模式是一个不错的选择。

发布-订阅本质也是观察者模式,但是他更加的松耦合,发布者和订阅者都不用清楚对方,全部由订阅中心做处理,这样耦合度就几乎没有了。

如图展示发布-订阅模式:

在 iOS 中,Notification 就是发布-订阅模式的一种实现,NSNotificationCenter 就类似订阅中心。

1
2
3
4
5
6
7
8
// 发布
[[NSNotificationCenter defaultCenter] postNotificationName:@"name_protocol" object:nil];
// 订阅
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onUpdate:)
name:@"name_protocol"
object:nil];

争论

网上有很多人说,观察者模式和发布-订阅模式是两种不同的设计模式,它们压根就是两码事,不能混为一谈。也有很多人说,两者其实都是观察者模式,只是实现手段有点不一样罢了,本质是一样的。

江湖纷争,众说纷纭!

设计模式是一种设计思想,在观察者模式基础上你可以衍生更多的设计模式和更多的设计思想。模式的实现手段可以多样化,没有最好只有更好,就好比 MVC、MVP、MVVM 等,你说它们是设计模式也好,是设计思路也罢,关键是利用它们有没有解决业务需求,为了模式而模式的设计华而不实!

个人觉得,发布-订阅模式只是观察者模式的一种实现手段,它本质还是观察者模式。


扫码关注,期待与你的交流~

坚持原创技术分享!