JS设计模式
设计模式:是一套被反复使用,多数人知晓的,经过分类的,代码设计经验的总结。换言之,就是用 “套路” 做事情。
- 观察者模式
- 发布 - 订阅者模式
观察者模式
我们假设你正在找一份软件工程师的工作,对“香蕉公司”很感兴趣。所以你联系了他们的HR,给了他你的联系电话。他保证如果有任何职位空缺都会通知你。这里还有几个候选人也你一样很感兴趣。所以职位空缺大家都会知道,如果你回应了他们的通知,他们就会联系你面试。
所以,以上和“观察者模式”有什么关系呢?这里的“香蕉公司”就是Subject,用来维护Observers(和你一样的候选人),为某些event(比如职位空缺)来通知(notify)观察者。
- 被观察者实例化 观察者实例化
- 被观察者添加观察者
- 被观察者改变状态,触发通知所有观察者的函数
简单实现就是被观察者里面存储了观察者的对象列表,当被观察者发生某种行为时,会回调观察者的方法。
// 被观察者对象
class Student {
constructor(name) {
this.s_name = name
this.state = '学习'
this.observer = [] // 观察者数组
}
// 获取状态的方法
getter() {
return this.state
}
// 改变状态的同时通过方法通知观察者
setState(state) {
this.state = state
this.notice()
}
// 添加观察者
attash(observer) {
this.observer.push(observer)
}
// 通知观察者
notice(observer) {
this.observer.forEach(v => {
v.sayHello(this.s_name, this.state)
});
}
}
// 观察者对象
class Teacher {
constructor(name) {
this.t_name = name
}
// 被观察者通知观察者执行的方法
sayHello(name, state) {
console.log('我是' + this.t_name + ',学生' + name + '现在的状态为:' + state);
}
}
// 实例化被观察者
stu = new Student('小明')
// 实例化观察者
t1 = new Teacher('王老师')
t2 = new Teacher('张老师')
// 添加观察者
stu.attash(t1)
stu.attash(t2)
// 被观察者触发了改变状态的方法,就会通知到观察者,观察者执行对应的方法
stu.setState('写作业')
发布 - 订阅者模式
在观察者模式中的Subject就像一个发布者(Publisher),而观察者(Observer)完全可以看作一个订阅者(Subscriber)。subject通知观察者时,就像一个发布者通知他的订阅者。这也就是为什么很多书和文章使用“发布-订阅”概念来解释观察者设计模式。但是这里还有另外一个流行的模式叫做发布-订阅设计模式。它的概念和观察者模式非常类似。最大的区别是:
在发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者(订阅者)。
意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。
那么如何过滤消息的呢?事实上这里有几个过程,最流行的方法是:基于主题以及基于内容。
发布 - 订阅模式分为3个状态
- 订阅
- 取消订阅
- 发布
// 发布-订阅模式
// 消息中心
function message1(data) { console.log('这是message1函数', data); }
function message2(data) { console.log('这是message2函数', data); }
function message3(data) { console.log('这是message3函数', data); }
function message4(data) { console.log('这是message4函数', data); }
function message5(data) { console.log('这是message5函数', data); }
function message6(data) { console.log('这是message6函数', data); }
class Observer {
constructor() {
this.message = []
}
// 订阅
on(type, fun) {
if (!this.message[type]) {
this.message[type] = []
}
this.message[type].push(fun)
}
// 取消订阅
off(type, fun) {
if (!this.message[type]) return
this.message[type] = this.message[type].filter(v => {
return v != fun
})
}
// 发布事件
emit(type, val) {
const data = {
type,
data: val
}
if (!this.message[type]) return
this.message[type].forEach(v => { // 基于type
v(data)
});
}
}
// 实例化发布者
const ob1 = new Observer()
// 发布者添加消息中心
ob1.on('type_a', message1)
ob1.on('type_a', message2)
ob1.on('type_a', message3)
ob1.on('type_a', message4)
ob1.on('type_b', message5)
ob1.on('type_b', message6)
// 实例化两个订阅者
const subscriber1 = {
name: '小明',
boo: true,
arr: [1, 2, 3]
}
const subscriber2 = {
name: '小红',
boo: false,
arr: [4, 5, 6]
}
// 发布者将订阅者的数据分发给消息中心
ob1.emit('type_a', subscriber1)
ob1.emit('type_b', subscriber2)
console.log('-----------------------------');
ob1.off('type_a', message1)
ob1.off('type_b', message6)
ob1.emit('type_a', subscriber1)
ob1.emit('type_b', subscriber2)
总结一下:
- 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
- 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
- 观察者模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。