当前位置: 首页 > news >正文

观察者模式实现非直接耦合

最近在看设计模式,一本《Head First 设计模式》,一本《javascript设计模式》,两本交替着看。Head First浅显易懂,代码用java实现,理解了一个设计模式的理念以后,先想想用js如何实现,然后再看js设计模式相关章节,感觉比以前看的时候理解深入了些。

今天早上看到颜海镜同学在早读课上分享的耦合关系一文,最后一种模块间非直接耦合的实现方式第一个让我想到的就是观察者模式。正好上午没事,就写了个demo实现了一下。

非直接耦合:两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。子模块无需知道对方的存在,子模块之间的联系,全部变成子模块和主模块之间的联系。

clipboard.png

现在要实现这样的功能:

  1. 所有模块只和主模块通讯,可以把自己的变化告知主模块,也可以从主模块接受信息并处理;

  2. 主模块负责监听所有其下的模块的变化,一旦认为该变化需要通知到指定一个或多个其他模块,就向这些模块发送消息。

大体实现思路是:

子模块包含一个主模块的引用,在实例化方法中将其对应的主模块实例作为参数赋值给自己的主模块引用。

constructor(hub) {
    this._hub = hub;
}
    

子模块统一实现监听方法observeFromHub,供主模块触发执行自己的观察方法,具体由子类实现。

observeFromHub() {
    throw new Error('no implementation.');
}

子模块统一实现将自己的变化告知主模块的方法update(value)

update(value){
    this._hub.observeFromModule(value);
}

主模块包含一个子模块列表

constructor(){
    this.modules = [];
}

子模块统一实现add/remove方法,将自己添加到主模块的子模块列表modules中。

add(hub) {
    var alreadyExists = hub.modules.some((el)=>el === this);
    if (!alreadyExists) hub.modules.push(this);
    return this;
}

remove(hub) {
    hub.modules = hub.modules.filter((el)=>el !== this);
    return this;
}

主模块实现触发子模块列表中所有子模块的监听方法。

deliver(data){
    this.modules.forEach((module) => module.observeFromHub(data));
    return this;
}

主模块实现监听方法observeFromModule,供子模块触发执行自己的观察方法,由此来感知子模块的变化,进而执行deliver方法通知其下所有子模块。

observeFromModule(data) {
    return this.deliver(data);
}

有些方法子模块是公用的,所以可以将这些公共方法提取出来作为子模块的抽象超类

CommonModule.js

module.exports = class CommonModule {
    constructor(hub) {
        this._hub = hub;
    }
    
    update(value){
        this._hub.observeFromModule(value);
    }

    observeFromHub() {
        throw new Error('no implementation.');
    }

    add(hub) {
        var alreadyExists = hub.modules.some((el)=>el === this);
        if (!alreadyExists) hub.modules.push(this);
        return this;
    }

    remove(hub) {
        hub.modules = hub.modules.filter((el)=>el !== this);
        return this;
    }
}

observeFromHub方法有子模块自己实现。这里创建两个子模块Module1和Module2。当修改Module1时,主模块通知Module2执行observerFromHub(value)方法:

Module1.js

var CommonModule = require('./CommonModule');
module.exports = class Module1 extends CommonModule{
    constructor(hub) {
        super(hub);
        this.inputValue = '';
    }

    update(value) {
        this.inputValue = value;
        console.log('module1 setInput start... :' + this.inputValue);
        super.update(value);
    }
}

Module2.js

var CommonModule = require('./CommonModule');
module.exports = class Module2 extends CommonModule{
    constructor(hub) {
        super(hub);
        this.outputValue = '';
    }

    observeFromHub(value) {
        this.outputValue = value;
        console.log('module2 received msg : ' + this.outputValue);
    }
}

主模块代码:

Hub.js

module.exports = class Hub {
    constructor(){
        this.modules = [];
    }

    observeFromModule(data) {
        return this.deliver(data);
    }
    
    deliver(data){
        this.modules.forEach((module) => module.observeFromHub(data));
        return this;
    }
}

客户端代码:

main.js

var Hub = require('./Hub');
var Module1 = require('./Module1');
var Module2 = require('./Module2');

var hub = new Hub;
var inputModule = new Module1(hub);
var outputModule = new Module2(hub);

outputModule.add(hub);

inputModule.update('this is m1 speaking...');

执行main.js结果:

module1 setInput start... :this is m1 speaking...
module2 received msg : this is m1 speaking...

这个例子中主模块既是观察者,观察ModuleA的变化,又是被观察者,被ModuleB观察着,A一有变化就会将信息发送给B。

还能想到的一些有趣变化:

  1. 主模块可以有多个,各自管辖的范围不同,但有些子模块可能会在多个范围中公用。

  2. 主模块中添加控制器,数据需不需要下发,下发到那几个子模块,由主模块控制。

相关文章:

  • 怎么把Maven项目转为动态Web项目?
  • mysql外键的使用
  • 08.Switch的使用方法
  • Python学习笔记11—函数
  • iOS - AppStores App 上架
  • 从输入 URL 到浏览器接收的过程中发生了什么事情
  • java设计模式类图大全
  • Spark Streaming 的玫瑰与刺
  • CentOS 6.4下Squid代理服务器的安装与配置
  • 0909滴滴面试小结
  • Mongodb 利用mongoshell进行数据类型转换
  • pom.xml标签以及maven在Idea使用
  • Android 样式和主题(style theme)
  • Linux作业7
  • 判断终端是ios还是安卓的一些妙用(附加微信分享图标修改)
  • [译]如何构建服务器端web组件,为何要构建?
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • IOS评论框不贴底(ios12新bug)
  • JAVA_NIO系列——Channel和Buffer详解
  • jquery cookie
  • linux安装openssl、swoole等扩展的具体步骤
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • Otto开发初探——微服务依赖管理新利器
  • Spark RDD学习: aggregate函数
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 第十八天-企业应用架构模式-基本模式
  • 分享几个不错的工具
  • 构建工具 - 收藏集 - 掘金
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 爬虫模拟登陆 SegmentFault
  • 浅谈web中前端模板引擎的使用
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 赢得Docker挑战最佳实践
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • 说说我为什么看好Spring Cloud Alibaba
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #include到底该写在哪
  • #Spring-boot高级
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (Java数据结构)ArrayList
  • (Ruby)Ubuntu12.04安装Rails环境
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (四)汇编语言——简单程序
  • (一)Java算法:二分查找
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)winform之ListView
  • .Mobi域名介绍
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET NPOI导出Excel详解