观察者模式做什么
观察者模式实现了对象之间的多对1依赖,一旦新数据发布所有订阅者都将收到通知并自动更新
观察者模式核心——interface
观察者模式-核心就是interface接口
为什么要设计接口?看个案例
现在有一个主题发布者Subject s,被一个订阅者Observer1 o1订阅。那么伪代码如下:
class Subject:
string topic
string content
string getTopic() { return topic}
string getContent() {return content }
void notifyAll() {
string t = getTopic()
string c = getContent()
// 调用o1的updateMyself方法更新o1
o1.updateMyself(t, c)
}
class Observer1:
void updateMyself(string topic, content) { ... }
某一天来了另外一个订阅者Observer2 o2,新增代码后变成:
class Subject:
string topic
string content
string getTopic() { return topic}
string getContent() {return content }
void notifyAll() {
string t = getTopic()
string c = getContent()
// 调用o1的updateMyself方法更新o1
o1.updateMyself(t, c)
// 调用o2的updateMe方法更新o2
o2.updateMe(t, c)
}
class Observer1:
void updateMyself(string topic, content) { ... }
class Observer2:
void updateMe(string topic, content) { ... }
由于Observer1和Observer2的更新方法不同,每来一个订阅者,Subject就不得不去适配该订阅者的更新方法:
- 假设不断地有新类型的订阅者过来,那么就要不断地往Subject.notifyAll里面加代码
- 假设不断地有订阅者退出,那么就要不断地往Subject.notifyAll里面删代码
- 假设不断地有订阅者修改了它们的update方法,那么就要不断地往Subject.notifyAll里面改代码
- 不支持运行时订阅者的增减
这就是面向每个订阅者的update()实现的编程,非常死板
没有接口,就没有协议。各玩各的一套,只能是疲于适配。因此,观察者需统一实现update接口
只要所有观察者将更新方法都统一一个函数签名,那对于Subject就非常友好了。Subject无需再关注每个订阅者具体的更新方法,因为订阅者的更新方法拥有相同的函数签名。代码如下:
class Subject:
string topic
string content
string getTopic() { return topic}
string getContent() {return content }
void notifyAll() {
string t = getTopic()
string c = getContent()
// 调用o1的update方法更新o1
o1.update(t, c)
// 调用o2的update方法更新o2
o2.update(t, c)
}
// 观察者interface
interface Observer {
void update(string topic, content)
}
class Observer1 implements Observer:
void update(string topic, content) { ... }
class Observer2 implements Observer:
void update(string topic, content) { ... }
但是,现在实现/取消订阅,还是通过在Subject.notifyAll()里面增加/删除代码来实现,扩展性很差
于是,Subject还需要一个注册订阅者的方法,需要一个删除订阅者的方法:
class Subject:
string topic
string content
list observers
string getTopic() { return topic}
string getContent() {return content }
void notifyAll() {
string t = getTopic()
string c = getContent()
for each o in observers {
o.update(t, c)
}
}
void addObserver(Observer o) {
observers.add(o)
}
void removeObserver(Observer o) {
observers.remove(o)
}
// 观察者interface
interface Observer {
void update(string topic, content)
}
class Observer1 implements Observer:
void update(string topic, content) { ... }
class Observer2 implements Observer:
void update(string topic, content) { ... }
好了,这个时候,如果这些订阅者还想去订阅其他subject呢?自然的思路是,其他subject的类也实现void addObserver(Observer o)
、void removeObserver(Observer o)
和void notifyAll()
——这又回到接口上来了。主题类也可以抽象成接口以便于扩展:
// 主题interface
interface Subject{
void notifyAll()
void addObserver(Observer o)
void removeObserver(Observer o)
}
// 观察者interface
interface Observer {
void update(string topic, content)
}
class ConcreteSubject implements Subject:
string topic
string content
list observers
string getTopic() { return topic}
string getContent() {return content }
void notifyAll() {
string t = getTopic()
string c = getContent()
for each o in observers {
o.update(t, c)
}
}
void addObserver(Observer o) {
observers.add(o)
}
void removeObserver(Observer o) {
observers.remove(o)
}
class Observer1 implements Observer:
void update(string topic, content) { ... }
class Observer2 implements Observer:
void update(string topic, content) { ... }
还有一个问题,上面的所有例子中,订阅者需要被通知什么信息,是通过Observer.update()的参数约定的,例子中通知的数据是topic和content
如果部分订阅者只需要topic,部分只需要content,部分还需要额外的数据呢?那void update(string topic, content)就不满足需求了
如何既保持统一的接口,又拥有各自的更新逻辑呢?——把整个"数据源"作为Observer.update()的参数:
// 主题interface
interface Subject{
void notifyAll()
void addObserver(Observer o)
void removeObserver(Observer o)
string getTopic()
string getContent()
}
// 观察者interface
interface Observer {
// 把整个"数据源"拉过来,把整个subject拿过来,想要什么数据自己内部取
void update(Subject s)
}
class ConcreteSubject implements Subject:
string topic
string content
list observers
string getTopic() { return topic}
string getContent() {return content }
void notifyAll() {
string t = getTopic()
string c = getContent()
for each o in observers {
o.update(this)
}
}
void addObserver(Observer o) {
observers.add(o)
}
void removeObserver(Observer o) {
observers.remove(o)
}
class Observer1 implements Observer:
void update(Subject s) {
string t = s.getTopic()
// update t ...
}
class Observer2 implements Observer:
void update(Subject s) {
string c = s.getContent()
// update c ...
}
小结
协商好接口(interface),所有实例都统一按照interface规定的标准去交互,避免了不同对象的method之间的差异,便于扩展
不同的对象按照interface去做各自的内部实现,它们将拥有相同的函数签名(method signature),内部却是不同的函数体(method bodies, aka implementation)——暴露出去的是公共的、统一的,内部逻辑是私有的、独占的notify无外乎两种方式:推和拉
推:需要事先约定好observer需要的数据,subject直接推送这些数据
拉:当不同的observer需要的数据在类型和数量上存在差异时,可以通过observer拉的方式:observer调用subject暴露的方法拉取自己所需的数据