文字介绍
Observer Pattern
这是观察者模式的英文名称
在此种模式中,一个目标对象管理所有相依于它的观察者对象,并在它本身的状态改变时主动通知观察者们
这个模式通常用来实现事件处理系统
有一家新闻报社,每天都会更新新闻内容,想要得到新闻报社发布的最新新闻内容需要提供联系方式给新闻报社,当新闻报社有新的新闻发布时会即时将新的新闻内容推送给所有订阅者,订阅者在收到新闻后,会阅读内容并做出反应
所以观察者模式又叫发布-订阅(Publish/Subscribe
)模式
先用Java
代码演示一下
定义两个抽象类
// 抽象订阅者
public interface Observer{
void update(String message);
}
// 抽象发布者
public interface Subject{
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
创建一个新闻报社
public class NewsAgency implements Subject{
//订阅者集合
private List<Observer> observers = new ArrayList<>();
//新闻内容
private String message;
//发布新的新闻
public void setMessage(String message) {
if (message == null) message = "今天无事发生";
this.message = message;
//通知
notifyObservers();
}
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
}
创建一个狗蛋订阅者
public class GouDanOffice implements Observer{
@Override
public void update(String message) {
System.out.println("狗蛋报社收到了新闻的消息,新闻内容是:" + message);
}
}
创建一个张三订阅者
public class ZhangSanOffice implements Observer {
@Override
public void update(String message) {
System.out.println("张三报社收到新闻消息,新闻内容是:"+message+",退订请打出㗊字");
}
}
接下来进行运行
public static void main(String[] args) {
//创建一个新闻报社
NewsAgency newsAgency = new NewsAgency();
newsAgency.setMessage("新闻报社正式成立");
//没有任何人订阅所以没人收到消息
GouDanOffice gouDanOffice = new GouDanOffice();
newsAgency.attach(gouDanOffice);
newsAgency.setMessage("感谢狗蛋报社订阅了服务");
//狗蛋收到了信息
System.out.println();
ZhangSanOffice zhangSanOffice = new ZhangSanOffice();
newsAgency.attach(zhangSanOffice);
newsAgency.setMessage("感谢张三报社订阅了服务");
//狗蛋和张三都收到了信息
}
运行结果如下
用JavaScript
代码演示
定义新闻报社
class NewsAgency{
constructor() {
this.observers = [];
}
set message(msg){
this.notifyObservers(msg);
}
//订阅
attach(observer) {
this.observers.push(observer);
}
detach(observer) {
this.observers = this.observers.filter(o => o !== observer);
}
notifyObservers(msg){
this.observers.forEach(o=>{
o.update(msg);
});
}
}
定义订阅者
class Observer{
constructor(name){
this.name = name;
}
update(msg){
console.log(this.name+'收到消息了,内容是:'+msg);
}
}
执行结果
let agency = new NewsAgency();
let goudan = new Observer('狗蛋');
//狗蛋进行了订阅
agency.attach(goudan);
//新闻报社发布了消息
agency.message = '新的消息';
//目前只有狗蛋订阅了所以只有狗蛋打印了欣慰内容
let dachui = new Observer('大锤');
//大锤也订阅了
agency.attach(dachui);
//发布了新的消息
//大锤和狗蛋同时收到了新闻的消息都进行了打印
agency.message = '新闻:震惊百分之九十九都不知道的这种事情';
//狗蛋退订了
agency.detach(goudan);
//再次发布新闻时只有大锤才能收到消息
agency.message = '新闻:震惊这件事情竟然每人都知道';
不过上方的js
代码是完全依据Java
进行改编的
实际开发中可以这样简单写一下
const newsAgency = {
observers:[],//订阅者
subscribe(fn){//订阅方法
this.observers.push(fn);
},
publish(message){
this.observers.forEach(fn => fn(message));
}
}
这样就做好了一个简单的观察者模式的代码
运行使用
newsAgency.subscribe( msg => console.log(`订阅者 1 收到信息:${msg}`) );
newsAgency.subscribe( msg => console.log(`订阅者 2 收到信息:${msg},这个是订阅者2`) );
//发布内容
newsAgency.publish('我是内容');
在实际开发过程里有很多地方都用到了观察者模式
一个典型的例子就是响应式数据和组件更新
当响应式数据发生变化时,依赖于该数据的组件会自动更新,这就是观察者模式的实现
例如下方的Vue
代码片段
<template>
<span>{{ message }}</span><br/>
<span>{{reversedMessage}}</span>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('')
}
}
}
</script>
这里有两个观察者
<span>
观察者响应message
的变化,并更新DOM
reversedMessage
响应message
的变化并进行计算操作,更新DOM
当我们更新message
的值时两个观察者会自动得到通知并更新
Vue
中广泛应用了观察者模式,从数据变化到视图更新,从组件转换到生命周期调用,观察者模式无处不在
Java
中也要观察者模式的身影
比方说在SpringBoot
中捕获运行异常
@Controller
public class TestController {
@RequestMapping("/test")
public String test() {
// ...
throw new RuntimeException("异常信息!");
}
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(Exception e) {
// 观察到RuntimeException异常,自动触发此方法进行处理
return "error";
}
}