资讯专栏INFORMATION COLUMN

从命令式到响应式 (二)

DataPipeline / 1198人阅读

摘要:知识点回顾,上次主要说了函数式和面向对象,命令式和响应式,系统和系统的差别。以内联的形式使用。响应式中的设计模式观察者模式在这种模式中,一个对象维持一系列依赖于它的对象,将有关的状态变更自动的通知给它们。

知识点回顾,上次主要说了函数式和面向对象,命令式和响应式,push 系统和 pull 系统的差别。在编程范式,风格之外,设计模式也是在程序设计中时时刻刻都在使用的东西,今天主要就讨论一下设计模式这个东西。

什么是设计模式

模式是一种可复用的解决方案,它有三大好处:

模式是已经得到验证的解决方案,因此我们可以在适合的场景中放心的使用它。

模式很容易被复用,是一种立即可用的解决方案,而且可以对其适当的修改以满足个性化的需求。

模式富有表达力,它通常有很良好的结构及已经设置好的表达方案的词汇,可以非常轻松的表达出程序员的意图。

其实我们每天都在接触模式,从最简单的facade(外观模式,jQuery,lodash为代表)到 singleton,再到MVC,模式可以说无处不再,当然还有rxjs中使用观察者模式等。有模式,就有反模式,既然模式可以带来好处,相应的反模式就会带来坏处,在javaScript中,以下就是我们经常见到的反模式:

在全局上下文中定义大量的变量来污染全局命名空间。这里的全局我们应该以相对的思维考虑,而不是特指window对象。例如:在angularjs 中一个controller 作为封闭的作用域,对于它内部定义的各种变量来说,这个作用域就是全局的,写过angularjs的同学应该遇到过,在一个controller中定义很多的变量,然后随着功能的增加,越来越难以维护。

修改类的原型,尤其是Object类,比修改更过份的是直接替换。

以内联的形式使用javaScript。

给setTimeout 或 setInterval传递字符串而不是函数,这会触发内部 eval 的执行。

响应式中的设计模式 观察者模式

在这种模式中,一个对象维持一系列依赖于它的对象,将有关的状态变更自动的通知给它们。当我们不再希望某个特定的观察者获取注册目标的对象时,它可以从目标的观察者列表中移除。代码如下:

观察者列表类,我们利用这个类来维护观察者的增删改查

class ObserverList {
    constructor() { };

    list = [];

    add(observer) {
        this.list.push(observer);
    }

    count() {
        return this.list.length;
    }

    get(index) {
        if(index > -1 && index < this.list.length) {
            return this.list[index];
        }else {
            return null;
        }
    }

    indexOf(observer, startIndex) {
        let i = startIndex;

        let pointer = -1;

        while( i< this.list.length) {
            if(this.list[i] === observer) {
                pointer = i;
            }
            i++;
        }

        return pointer;
    }

    removeIndexAt(index) {
        if(index === 0) {
            this.list.shift();
        }else if (index === this.list.length - 1) {
            this.list.pop();
        }
    }
}

主题类,利用这个类来维护一个观察目标,使用观察者列表类来维护其自己的观察者,通过观察者提供的接口向外发送目标上发生的变化。

class Subject {
    constructor() {
        this.observers = new ObserverList();
    }

    addObserver(observer) {
        this.observers.add(observer);
    }

    removeObserver(observer) {
        this.observers.removeIndexAt(this.observers.indexOf(observer, 0));
    }

    notify(context) {
        const count = this.observers.count();

        for(let i = 0; i< count; i++) {
            this.observers.get(i).update(context);
        }
    }
}

观察者类,为目标发生变化时需要获得通知的对象提供一个更新接口。

class Observer {
    constructor() { }

    update() {
      // 获取通知的接口, 不同的observe 可以针对性的设置更新逻辑
    }
}

然后我们就可以利用定义好的这些类,实现一些功能,例如,一个主checkbox,当它的状态变化时通知页面上其它的checkbox检查状态,代码大致如下:

HTML代码



javaScript代码

const box = document.getElementById("box");
const btn = document.getElementById("button");
const container = document.getElementById("container");

// 工具函数
function extend(source, target) {
    for (let key in source) {
        target[key] = source[key];
    }
}

// 利用工具函数来扩展DOM元素
extend(new Subject(), box);

// 将点击事件通知给观察者
box.onclick = function {
    box.notify(box.checked);
}

btn.onclick = function addNewObserver() {
    const check = document.createElement("input");

    check.type = "checkbox";

    extend(new Observer(), check);

    // 重写自定义的更新行为
    check.update = (value) => this.checked = value;

    // 为subject的观察者列表中添加新的观察者
    box.addObserver(check);

    // 将观察者附加到容器上
    container.appendChild(check);
}
发布/订阅模式

观察者模式要求希望接收通知的观察者必须订阅内容改变的事件,而发布/订阅模式中添加了一个事件通道,此通道介于订阅者和发布者之间,这样设置的主要目的是促进发布者和接收者之间的松散耦合,避免订阅者和发布者产和直接的联系,如图:

Observer Pattern

      /----<--Subscribe--<--
    Subject             Observer
      --->--Fire Event-->--/

Publish/Subscribe Pattern

                                    /----<--Subscribe--<--
    Publisher-->--publish-->---Event Channel         Subscriber
                                    --->---fire event-->--/

在实际的应用中, 这两种模式可以结合使用,它们都鼓励开发者思考应用程序之间不同部分之间的关系,将应用程序分解为更小,更松散耦合的块以提高代码的复用。

这两种模式的优缺点

优点

只需要维护各个对象之间的通信接口的一致性,而无需紧密耦合。

观察者和目标之间可以建立起一种动态的关系,这提供了很大的灵活性,在程序的各部分紧密耦合时,要实现这种动态关系是非常不容易的。

缺点

在发布/订阅模式中,由于解耦了发布者和订阅者,有时会难以保证程序按照我们的预期进行。例如,发布者会假设有人在订阅它们,当订阅者发生错误后,由于系统的解耦,发布者并不会看到这一点。

订阅者之间非常无视彼此的存在,对于变换发布者产生的成本视而不见,由于它们之间动态的关系,难以跟踪依赖更新。

rxjs的实现就是建立在这两种模式的基础之上,预先要了解的基本知识通过这两篇基本上介绍完了,当然都是走马观花式的,基中任何一个点拿出来都可以长篇大论,各位如有兴趣可以找资料深入研究。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97264.html

相关文章

  • Spring Boot 2 快速教程:WebFlux 快速入门(

    摘要:响应式编程是基于异步和事件驱动的非阻塞程序,只是垂直通过在内启动少量线程扩展,而不是水平通过集群扩展。三特性常用的生产的特性如下响应式编程模型适用性内嵌容器组件还有对日志消息测试及扩展等支持。 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 02:WebFlux 快速入门实践 文章工程: JDK...

    gaara 评论0 收藏0
  • 命令响应(一)

    摘要:响应式命令式这两种编程风格的思维方式是完全相反的。第二种方式是工人主动去找工人索取生产手机所要的零件,然后生产一台完整的手机,这两种方式就对应的响应式和命令式。 angular2中内置了rxjs,虽然框架本身并没有强制开发者使用响应式风格来组织代码,但是从框架开发团队的角度可以看出他们必然是认同这种编程风格的。rxjs本质是基于函数式编程的响应式风格的库,函数式相对于面向对象来说更加抽...

    JayChen 评论0 收藏0
  • javascript高级学习总结(

    摘要:那个率先改变的实例的返回值,就会传递给的回调函数。函数对函数的改进,体现在以下四点内置执行器。进一步说,函数完全可以看作多个异步操作,包装成的一个对象,而命令就是内部命令的语法糖。中的本质就是没有的隐藏的组件。 1、原型 - jquery使用showImg(https://segmentfault.com/img/bVbwNcY?w=692&h=442);注释 : 实例虽然不同,但是构...

    Songlcy 评论0 收藏0
  • [译] 前端攻略-路人甲英雄无敌:JavaScript 与不断演化的框架

    摘要:一般来说,声明式编程关注于发生了啥,而命令式则同时关注与咋发生的。声明式编程可以较好地解决这个问题,刚才提到的比较麻烦的元素选择这个动作可以交托给框架或者库区处理,这样就能让开发者专注于发生了啥,这里推荐一波与。 本文翻译自FreeCodeCamp的from-zero-to-front-end-hero-part。 继续译者的废话,这篇文章是前端攻略-从路人甲到英雄无敌的下半部分,在...

    roadtogeek 评论0 收藏0
  • 命令响应(六)

    摘要:从这个系列的第一章开始到第五章,基于的响应式编程的基础知识基本上就介绍完了,当然有很多知识点没有提到,比如,等,不是他们不重要,而是碍于时间精力等原因没办法一一详细介绍。 从这个系列的第一章开始到第五章,基于rxjs的响应式编程的基础知识基本上就介绍完了,当然有很多知识点没有提到,比如 Scheduler, behaviorSubject,replaySubject等,不是他们不重要,...

    sanyang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<