发布—订阅模式
发布订阅模式指的是希望接收通知的对象(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 运算符,那么就无法获取到实例,但是使用安全模式进行实例化,就能很好地解决这个问题。