# 观察者模式

# 介绍

  • 发布 & 订阅
  • 一对多

# 示例

  • 点咖啡,点好之后坐等被叫

# UML 类图

在这里插入图片描述

# 代码演示

// 主题,保存状态,装填变化之后触发所有观察者对象
class Subject {
  constructor() {
    this.state = 0
    this.observers = []
  }
  getState() {
    return this.state
  }
  setState(state) {
    this.state = state
    this.notifyAllObservers()
  }
  notifyAllObservers() {
    this.observers.forEach(observer => {
      observer.update()
    })
  }
  attach(observer) {
    this.observers.push(observer)
  }
}

// 观察者
class Observer {
  constructor(name, subject) {
    this.name = name
    this.subject = subject
    this.subject.attach(this)
  }
  update() {
    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(2) 
// o1 update, state: 2
// o2 update, state: 2
// o3 update, state: 2

# 场景

# 网页事件绑定

<button id="btn1">btn</button>
<script>
  $('#btn1').click(function() {
    console.log(1)
  })
  $('#btn1').click(function() {
    console.log(2)
  })
  $('#btn1').click(function() {
    console.log(3)
  })
</script>

# Promise

function loadImg(src) {
  let promise = new Promise((resolve, reject) => {
    let img = document.createElement('img')
    img.onload = function () {
      resolve(img)
    }
    img.onerror = function () {
      reject('图片加载失败')
    }
    img.src = src
  })
  return promise
}

let src = 'https://img.alicdn.com/tfs/TB1Ly5oS3HqK1RjSZFPXXcwapXa-238-54.png'
let result = loadImg(src)


result.then(function(img) {
  console.log(`width:${img.width}`)
  return img
}).then(function(img) {
  console.log(`height:${img.height}`)
}).catch(function(e) {
  console.log(e)
})

# jQuery callbacks

var callbacks = $.Callbacks() //注意大小写
callbacks.add(function (info) {
  console.log('fn1', info)
})
callbacks.add(function (info) {
  console.log('fn1', info)
})
callbacks.add(function (info) {
  console.log('fn1', info)
})
callbacks.fire('gogogo')
callbacks.fire('fire')

# nodejs 自定义事件

# 监听触发事件
const EventEmiter = require('events').EventEmitter

const emitter1 = new EventEmiter()

// 监听 some 事件
emitter1.on('some', info => {
  console.log('fn1', info)
})
// 监听 some 事件
emitter1.on('some', info => {
  console.log('fn2', info)
})
// 触发 some 事件
emitter1.emit('some', 'xxxx')
// fn1 xxxx
// fn2 xxxx
# 继承监听触发事件
// 继承
const EventEmiter = require('events').EventEmitter

class Dog extends EventEmiter {
  constructor(name) {
    super()
    this.name = name
  }
}

const d = new Dog('dog')

d.on('bark', function() {
  console.log(this.name, 'bark-1')
})
d.on('bark', function() {
  console.log(this.name, 'bark-2')
})


setInterval(function() {
  d.emit('bark')
}, 1000)
# 流读取文件
// stream 用到自定义事件

const fs = require('fs')
const readStream = fs.createReadStream('./data/file.txt')

let length = 0

readStream.on('data', function (chunk) {
  let len = chunk.toString().length
  console.log('len', len)
  length += len
})
readStream.on('end', function() {
  console.log('length', length)
})

// 监听每一行
const fs = require('fs')

const readline = require('readline')

let rl = readline.createInterface({
  input: fs.createReadStream('./data/file.txt')
})

let lineNum = 0
rl.on('line', function(line) {
  // line 就是每一行的数据
  lineNum++
})
rl.on('close', function() {
  console.log('linenum', lineNum)
})

# 其他场景

  • nodeJS 中:处理 http 请求;多进程通讯
// http 处理请求
function serverCallback(req, res) {
  var method = req.method.toLowerCase() // 获取请求的方法
  if(method === 'get') {
    // 处理get请求的代码
  }
  if(method === 'post') {
    // 接收 post 请求的内容
    var data = ''
    req.on('data', function(chunk) {
      // 一点一点 接收内容
      data += chunk.toString()
    })
    req.on('end', function() {
      // 接收完毕,将内容输出
      res.writeHead(200, {'Content-type': 'txt/html'})
      res.write(data)
      res.end()
    })
  }
}

// 多进程通讯
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: 'work'})

// sub.js
process.on('message', function(m) {
  console.log('CHILD go 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}></header>
      </div>
    )
  }
  componentDidMount() {
    // 判断是否已经登录
    this.doCheck()
  }
}
  • vue watch
var vm = new Vue({
  el: '#demo',
  data: {
    fileName: 'Foo',
    laseName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName(val) {
      this.fullName = val + ' ' + this.laseName
    },
    lastName(val) {
      this.fullName = this.firstName + '' + val
    }
  }
})

# 设计原则验证

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

评 论:

更新: 11/21/2020, 7:00:56 PM