发布—订阅模式
发布订阅模式指的是希望接收通知的对象(Subscriber)基于一个主题通过自定义事件订阅主题,被激活事件的对象(Publisher)通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。
const fs = require('fs')
let events = {
arr: [],
on(callback){
this.arr.push(callback)
},
emit(){
this.arr.forEach(fn => fn())
}
}
events.on(function(){
console.log('订阅'); //订阅事件
})
fs.readFile('./demo1.txt','utf8',function(err,data){
events.emit(); //发布
})
fs.readFile('./demo2.txt','utf8',function(err,data){
events.emit(); //发布
})
首先看一个简单的例子:定义一个对象events,带有arr属性用于存放要执行的回调数组;on和emit用于定义订阅和发布的方法。
events.on()定义了订阅方法,并且将要执行的函数存放到数组中去;events.emit()做发布行为,每次发布时都会让events.on中的回调执行一下,所以上面的代码执行结果就是:
//订阅
//订阅
下面再来修改一下回调的参数,在发布时将读取的data传递进去:
fs.readFile('./demo1.txt','utf8',function(err,data){
events.emit(data);
})
fs.readFile('./demo2.txt','utf8',function(err,data){
events.emit(data);
})
在events中新增dataSource
属性,存放数据源:
let events = {
dataSource: [],
arr: [],
on(callback){
this.arr.push(callback)
},
emit(data){
this.dataSource.push(data);
this.arr.forEach(fn => fn(this.dataSource))
}
}
这样做的目的是在订阅时能自己控制数据:
events.on(function(data){
if(data.length === 2){
console.log('订阅',data); //订阅事件
}
})
一般发布订阅用于解耦合,主要还是基于回调函数实现。
可以不订阅,只发布;发布和订阅是分开的,并且二者之间不产生关系。
全部代码如下:
const fs = require('fs')
let events = {
dataSource: [],
arr: [],
on(callback){
this.arr.push(callback)
},
emit(data){
this.dataSource.push(data)
this.arr.forEach(fn => fn(this.dataSource))
}
}
events.on(function(data){
console.log('订阅',data); //订阅事件
})
fs.readFile('./demo1.txt','utf8',function(err,data){
events.emit(data);
})
fs.readFile('./demo2.txt','utf8',function(err,data){
events.emit(data);
})
观察者模式
观察者模式就是存储状态变化,当被观察者状态发生改变时,向观察者逐一发出通知。
class Subject{ //目标对象类
constructor(name){
this.name = name;
this.state = 1;
this.arr = [];
}
attach(){
this.arr.push(o);
}
setState(state){
this.state = state;
this.arr.forEach(o => o.updateState(state))
}
}
class Observer{ //观察者类
constructor(name){
this.name = name;
}
updateState(state){
console.log(this.name + '收到状态改变是:' + state)
}
}
let s = new Subject();
let o1 = new Observer('观察者1');
let o2 = new Observer('观察者2');
s.attach(o1); //被观察的目标对象注册观察者
s.attach(o2);
s.setState(2); //通知所有的观察者,状态改变
//输出结果
观察者1收到状态改变是:2
观察者2收到状态改变是:2
观察者和被观察者有关联存在;观察者要放到被观察者的数组中。
vue
就是基于观察者模式,当某个数据一变化时,即通知视图更新;另外,观察者模式其实包含发布订阅模式。
工厂模式
工厂模式是用来创建对象的一种最常用的设计模式,不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。
简单工厂
let Factory = function(role){
function Admin(){
this.name = '超级管理员';
this.authority = ['首页','产品列表','产品详情','用户管理','权限管理'];
}
function Manager(){
this.name = '管理员';
this.authority = ['首页','产品列表','产品详情','用户管理'];
}
function User(){
this.name = '普通用户';
this.authority = ['首页','产品列表','产品详情'];
}
switch(role){
case 'Admin':
return new Admin();
case 'Manager':
return new Manager();
case 'User':
return new User();
default:
throw new Error('参数错误,请选择:Admin,Manager,User');
}
}
let admin = Factory('Admin');
let manager = Factory('Manager');
let user = Factory('User');
通过对传递参数的判定,返回不同的对象实例,但是这三种实例的内部构造都很相似,因此还可以对其进行优化。
工厂方法
工厂方法的本意是将实际创建对象的工作推迟到子类中,这样使得核心类变为抽象类。
在工厂函数的原型中设置所有对象的构造函数,这样便于后期扩展。
let Factory = function (role) {
//安全模式创建工厂方法函数
if (this instanceof Factory) {
let f = new this[role]();
return f;
} else {
return new Factory(role);
}
}
//工厂方法原型设置对象构造函数
Factory.prototype = {
Admin: function () {
this.name = '超级管理员';
this.authority = ['首页', '产品列表', '产品详情', '用户管理', '权限管理'];
},
Manager: function () {
this.name = '管理员';
this.authority = ['首页', '产品列表', '产品详情', '用户管理'];
},
User: function User() {
this.name = '普通用户';
this.authority = ['首页', '产品列表', '产品详情'];
}
}
let admin = Factory('Admin');
let manager = Factory('Manager');
let user = Factory('User');
在上述调用函数过程中,一旦在任何阶段忘记使用new运算符,那么就无法获取到实例,但是使用安全模式进行实例化,就能很好地解决这个问题。