【知识笔记】编程心法—设计模式之观察者模式

文字介绍

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执行结果
不过上方的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('我是内容');

js2运行结果
在实际开发过程里有很多地方都用到了观察者模式

一个典型的例子就是响应式数据和组件更新
当响应式数据发生变化时,依赖于该数据的组件会自动更新,这就是观察者模式的实现

例如下方的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>

这里有两个观察者

  1. <span>观察者响应message的变化,并更新DOM
  2. 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";
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值