JavaScript 设计模式----观察者模式

1. 观察者模式

1.1 观察者模式介绍

  • 发布 & 订阅
  • 一对多
  • 示例
    • 点咖啡,点好之后坐等被叫

1.2 观察者模式类图

  • 传统 UML 类图
    在这里插入图片描述
  • 简化后的 UML 类图
    在这里插入图片描述
    • 左侧 Observer观察者,有一个 update() 触发方法
    • 右侧 Subject主题,可以绑定多个观察者
      • Subject 中的 observers 是个数组 Array
      • 能够获取状态 getState()
      • 能够设置状态 setState()
      • 设置状态后,会触发所有的观察者里的 update 方法

1.3 观察者模式演示

// 主题,保存状态,状态变化之后触发所有观察者对象
class Subject {
    
    
	constructor() {
    
    
		this.state = 0
		this.observers = []
	}
	getState() {
    
    
		return this.state
	}
	setState(state) {
    
    
		this.state = state
		this.notifyAllObservers()
	}
	notifyAllObservers() {
    
     // 此方法将当前所有观察者 observers,进行遍历
		this.observers.forEach(observer => {
    
     // 每个观察者都执行 update() 方法
			observer.update()
		})
	}
	attach(observer) {
    
     // 添加新的观察者
		this.observers.push(observer)
	}
}

// 观察者
class Observer {
    
    
	constructor(name, subject) {
    
    
		this.name = name
		this.subject = subject
		this.subject.attach(this) // 把观察者添加到当前主题中来
	}
	update() {
    
     // 当触发观察者的 update 方法之后,把 Subject 中的状态 state 打印出来
		console.log(`${
      
      this.name} update, state: ${this.subject.getState()`)
	}
}

// 测试
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)

s.setState(1)
s.setState(2)
s.setState(3)

1.4 观察者模式场景

1.4.1 网页事件绑定
  • 网页事件绑定以及所有的UIwebview这种层面的事件监听的机制全部用观察者模式
    • 比如监听用户点击、拖拽
  • 如果不用观察者模式,那代码开发就会比较难理解
<button id="btn1">btn</button>

<script>
	$('#btn1').click(function () {
     
      // 等待按钮被点击后才会触发,如果不点击,就永远不会触发
		console.log(1)
	})
	$('#btn1').click(function () {
     
     
		console.log(2)
	})
	$('#btn').click(function () {
     
     
		console.log(3)
	})
</script>
1.4.2 Promise
// 如何生成一个Promise,并且返回Promise
function loadImg(src) {
    
    
	var promise = new Promise(function (resolve, reject) {
    
    
		var img = document.createElement('img')
		img.onload = function () {
    
    
			resolve(img)
		}
		img.onerror = function () {
    
    
			reject('图片加载失败')
		]
		img.src = src
	})
	return promise
}
// 发布订阅的关系
var src = 'https://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
    
     // 先绑定函数,不会立刻执行,因为加载图片是需要时间去处理
	console.log('width', img.width)
	return img
}).then(function (img) {
    
     // 需要等Promise的状态变化才执行
	console.log('height', img.height)
})
1.4.3 jQuery 中 callbacks
// 自定义事件,自定义回调
var callbacks = $.Callbacks() // 注意大小写
callbacks.add(function (info) {
    
    
	console.log('fn1', info)
})
callbacks.add(function (info) {
    
    
	console.log('fn2', info)
})
callbacks.add(function (info) {
    
    
	console.log('fn3', info)
})
callbacks.fire('gogogo')
callbacks.fire('fire')
1.4.4 Nodejs 自定义事件
const EventEmitter = require('events').EventEmitter // 引用基础api

const emitter1 = new EventEmitter()
emitter1.on('some', () => {
    
    
	// 监听 some 事件
	console.log('some event is occured 1')
})
emitter1.on('some', () => {
    
    
	// 监听 some 事件
	console.log('some event is occured 2')
})
// 触发 some 事件
emitter1.emit('some')
const EventEmitter = require('events').EventEmitter
const emitter = new EventEmitter()
emitter.on('showName', name => {
    
    
	console.log('event occured', name)
})
emitter.emit('showName', 'zhangsan') // emit 时候可以传递参数过去
  • EventEmitter 事件被其他功能继承使用
const EventEmitter = require('events').EventEmitter

// 任何构造函数都可以继承 EventEmitter 的方法 on emit
class Dog extends EventEmitter {
    
    
	constructor(name) {
    
    
		super()
		this.name = name
	}
}
var simon = new Dog('simon')
simon.on('back', function () {
    
    
	console.log(this.name, 'barked')
})
setInterval(() => {
    
    
	simon.emit('back')
}, 500)
// Stream 用到了自定义事件,监听流数据
var fs = require('fs')
var readStream = fs.createReadStream('./data/file1.txt') // 读取文件的 Stream

var length = 0
readStream.on('data', function (chunk) {
    
    
	length += chunk.toString().length
})
readStream.on('end', function () {
    
    
	console.log(length)
})
// readline 用到了自定义事件,监听行数据
var readline = require('readline');
var fs = require('fs')

var rl = readline.createInterface){
    
    
	input: fs.createReadStream('./data/file1.txt')
});

var lineNum = 0
rl.on('line', function(line) {
    
    
	lineNum++
});
rl.on('close', function() {
    
    
	console.log('lineNum', lineNum)
});
1.4.5 其他场景
  • Node.js 中:处理 http 请求;多进程通讯
// 处理 http 请求
function serverCallback(req, res) {
    
    
	var method = req.method.toLowerCase() // 获取请求的方法
	if (method === 'get') {
    
    
		// 省略 3 行,上文代码示例中处理 GET 请求的代码
	}
	if (method === 'post') {
    
    
		// 接收 post 请求的内容
		var data = ''
		req.on('data', function (chunk) {
    
    
			// "一点一点" 接收内容
			data += chunk.toString()
		})
		req.on('end', function () {
    
    
			// 接收完毕,将内容输出
			res.writeHead(200, {
    
    'Content-type': 'text/html'})
			res.write(data)
			res.end()
		})
	}
}
// 多进程通讯 parent.js
var cp = require('child_process')
var n = cp.fork('./sub.js')
n.on('message', function (m) {
    
    
	console.log('PARENT got message: ' + m)
})
n.send({
    
    hello: 'world'})

// sub.js
process.on('message', function (m) {
    
    
	console.log('CHILD got message: ' + m)
})
process.send({
    
     foo: 'bar' })

  • Vue 和 React 组件生命周期触发
// React
class Login extends React.Component {
    
    
	constructor(props, context) {
    
    
		super(props, context);
		this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
		this.state = {
    
    
			checking: true
		}
	}
	render() {
    
    
		return (
			<div>
				<Header title="登录" history={
    
    this.props.history}/>
			</div>
		)
	}
	componentDidMount() {
    
    
		// 判断是否已经登录
		this.doCheck()
	}
}
  • Vue watch
// Vue
var vm = new Vue({
    
    
	el: '#demo',
	data: {
    
    
		firstName: 'Foo',
		lastName: 'Bar',
		fullName: 'Foo Bar'
	},
	watch: {
    
    
		firstName: function (val) {
    
    
			this.fullName = val + ' ' + this.lastName
		},
		lastName: function (val) {
    
    
			this.fullName = this.firstName + ' ' + val
		}
	}
})

1.5 观察者模式设计原则验证

  • 主题和观察者分离,不是主动触发而是被动监听,两者解耦
  • 符合开放封闭原则

猜你喜欢

转载自blog.csdn.net/qq_43645678/article/details/114578232