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