1.概述
观察者模式又称为发布订阅者模式。该模式在日常生活中十分常见,小白敲了十年的代码终于攒够了钱准备去买一辆跑车,但是当小白到4s店被告知现在没有这辆车了,销售喊小白留一下自己的电话当有新车的时候通知小白。在这个例子中,小白和其它想要购买这辆跑车的顾客都为订阅者,4s店则是发布者,当有新车时通过电话或短信的形式发布消息给顾客。
2.代码实现思路
在同一个4S店中会有多个顾客等待同一辆车,若是不规定发送消息的方式可能会非常乱,有的顾客选择邮箱接收消息,有的顾客选择手机短信,有的顾客选择微信通知.....,我们需要定义一个订阅者接口ListenersInterface规范这些方式,统一选择打电话通知,最好给4S店也定义一个接口EventMangerInterface对4S店进行规范,EventMangerInterface接口中定义一个存放订阅者的数下,新增订阅者,删除订阅者,以及通知订阅者的方法,在ListenersInterface中定义一个接收通知的方法,当发布者调用通知订阅者的方法后,订阅者会通过该方法来进行接收。
3.代码实现
接下来将通过一个监听文件打开关闭的代码来实现该模式
1.发布者代码
// 订阅类型
type listenerType = 'close' | 'open'
// 发布者接口
interface EventManagerInterface {
// 订阅者
listeners: Map<listenerType, ObserverInterface[]>
// 加入订阅
subscribe(type: listenerType, listener: ObserverInterface): void
// 取消订阅
unSubscribe(type: listenerType, listener: ObserverInterface): void
// 发布消息
notify(type: listenerType): void
}
export class FilePublish implements EventManagerInterface{
listeners: Map<listenerType, ObserverInterface[]> = new Map()
notify(type: listenerType): void {
let currentListeners = this.listeners.get(type)
if(!currentListeners) {
throw Error('no found listener in this instance')
}
currentListeners.forEach((item: ObserverInterface)=>{
item.update()
})
}
subscribe(type: listenerType, listener: ObserverInterface): void {
let currentListeners = this.listeners.get(type)
if(!currentListeners) {
currentListeners = []
}
if(currentListeners.findIndex(item=>listener === item) === -1) {
currentListeners.push(listener)
}
this.listeners.set(type, currentListeners)
}
unSubscribe(type: listenerType, listener: ObserverInterface): void {
let currentListeners = this.listeners.get(type)
if(!currentListeners) {
throw Error('no found Listener in this instance')
}
let index = currentListeners.findIndex(item=>listener === item)
if(!index) {
throw Error('no found Listener in this instance')
}
currentListeners = currentListeners.splice(index, 1)
this.listeners.set(type, currentListeners)
}
// 打开文件
async openFile() {
this.notify('open')
}
// 关闭文件
async closeFile() {
this.notify('close')
}
}
2.订阅者代码
// 订阅者接口
interface ObserverInterface {
update(): void
}
// 监听文件打开类
export class FileOpenListener implements ObserverInterface {
update() {
alert('文件打开了')
}
}
// 监听文件关闭类
export class FileCloseListener implements ObserverInterface {
update() {
alert('文件关闭了')
}
}
3. 客户端代码
<template>
<button @click="onOpen">打开文件</button>
<button @click="onClose">关闭文件</button>
</template>
<script setup lang="ts">
import {FileCloseListener, FileOpenListener, FilePublish} from "@/util/ObserverPattern";
const filePublish = new FilePublish()
const fileCloseListener = new FileCloseListener()
const fileOpenListener = new FileOpenListener()
filePublish.subscribe('open', fileOpenListener)
filePublish.subscribe('close', fileCloseListener)
const onOpen = ()=>{
filePublish.openFile()
}
const onClose = ()=>{
filePublish.closeFile()
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
4.模式的优缺点
1. 优点
符合开闭原则,订阅者或者发布者需要添加功能时,只需要实现接口进行扩展即可,不需要修改源代码。
2.缺点
该模式不能对订阅者进行排序,若是车少想买车的顾客多时很容易发生冲突。
5.结语
小白每周尽量会多生产几篇设计模式内容,若对设计模式的理解有不正确之处请评论指正,小白会虚心接受,教学相长也。