• 设计模式-观察者模式


    介绍

    • 发布 & 订阅
    • 一对多

    UML类图

    • 传统UML类图
      传统UML类图
    • 简化后UML类图
      简化后的UML类图

    代码演示

    // 主题,保存状态,状态变化之后触发所有观察者对象
    class Subject {
        contructor() {
            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.observer.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(1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    场景

    • 网页事件绑定
    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 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){
        console.log('height', img.height)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • nodejs自定义事件
    const EventEmitter = require('events').EventEmitter
    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')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    const EventEmitter = require('events').EventEmitter
    const emitter = new EventEmitter()
    emitter.on('shonName', name => {
        console.log('event occured', name)
    })
    emitter.emit('showName', 'zhangsan') // emit时候可以传递参数过去
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    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('bark', function(){
        console.log(this.name, 'barked')
    })
    setInterval(() => {
        simon.emit('bark')
    }, 500)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    // 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)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    // 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    其他场景

    • nodejs中:处理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()
            })
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    // parent.js
    var cp = require('child_process')
    var n = cp.for('./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 got message: ' + m)
    })
    process.send({foo: 'bar'})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • vue和React组件生命周期触发
    class Login extends React.Component {
        constructor(props, context) {
            super(props, context);
            this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            this.state = {
                checking: false
            }
        }
        render() {
            return (
                <div>
                    <Header title="登录" history={this.props.history} />
                </div>
            )
        }
        componentDidMount() {
            // 判断是否已经登录
            this.doCheck()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • vue watch
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    设计原则验证

    • 主题和观察者分离,不是主动触发而是被动监听,两者解耦
    • 符合开放封闭原则
  • 相关阅读:
    变分法 (Calculus of Variations)
    【JS】Chapter11-正则&阶段案例
    论文阅读:Graphics2RAW: Mapping Computer Graphics Images to Sensor RAW Images
    关于加密通道规范,你真正用的是TLS,而非SSL
    RouYi-Vue前后端分离 --- 项目的启动、登录功能是怎么实现的、怎么样创建新模块
    分类预测 | Matlab实现SSA-SVM多特征分类预测
    【JAVA高级】——初识JDBC中DAO数据访问对象
    start()方法源码分析
    专访清华裘捷中:亚洲高校首个KDD最佳博士论文奖是如何炼成的?
    适配LVGL界面图片和文字显示很虚,色阶明显的解决方法(全志R128适用)
  • 原文地址:https://blog.csdn.net/linanran1027/article/details/133098204