当前位置: 首页 > 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还是安卓的一些妙用(附加微信分享图标修改)
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 3.7、@ResponseBody 和 @RestController
  • Elasticsearch 参考指南(升级前重新索引)
  • ES6简单总结(搭配简单的讲解和小案例)
  • js作用域和this的理解
  • log4j2输出到kafka
  • Python3爬取英雄联盟英雄皮肤大图
  • vagrant 添加本地 box 安装 laravel homestead
  • 从PHP迁移至Golang - 基础篇
  • 当SetTimeout遇到了字符串
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 基于web的全景—— Pannellum小试
  • 聊聊directory traversal attack
  • 前端攻城师
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 小而合理的前端理论:rscss和rsjs
  • 学习ES6 变量的解构赋值
  • 一些css基础学习笔记
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #Ubuntu(修改root信息)
  • #前后端分离# 头条发布系统
  • ${factoryList }后面有空格不影响
  • (03)光刻——半导体电路的绘制
  • (1)(1.11) SiK Radio v2(一)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (二)c52学习之旅-简单了解单片机
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (一)Neo4j下载安装以及初次使用
  • (一)WLAN定义和基本架构转
  • .gitattributes 文件
  • .NET Micro Framework初体验
  • .net MySql
  • .NET 分布式技术比较
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化