01 September 2016
号外号外:我的新书《现代JavaScript库开发:原理、技术与实战》出版啦!!!快点我查看
号外号外:一组小而美的JavaScript迷你库!!!快点我查看
号外号外:猿辅导招聘前端,后端,客户端啦!地点:北京!!!快点我查看

之前组内同学问我耦合的关系,我没给对方讲清楚,今天借这个机会来深入讲讲模块之间的耦合关系这个事情。

本文将用图文详细讲解七种耦合的不同之处。

高内聚与低耦合

高内聚与低耦合是每个软件开发者追求的目标,那么内聚和耦合分别是什么意思呢?

内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。

耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。

耦合

不同模块之间的关系就是耦合,根据耦合程度可以分为 7 种,耦合度依次变低。

  • 内容耦合
  • 公共耦合
  • 外部耦合
  • 控制耦合
  • 标记耦合
  • 数据耦合
  • 非直接耦合

下面我们来说说每种耦合是什么,开始之前先来说下要实现的功能。m1 和 m2 是两个独立的模块,其中 m2 种会显示 m1 的输入,m1 会显示 m2 的输入。

很显然,m1 和 m2 两个模块之间会有一些联系(耦合),你也可以想想如何实现这个功能,下面用 7 种不同的方式来实现这个功能。

注:项目的代码我放到了github,项目的 demo,可以在这里查看

内容耦合

内容耦合是最紧的耦合程度,一个模块直接访问另一模块的内容,则称这两个模块为内容耦合。

为了实现功能,我们将 m1 的输入放到 m2.m1input 上,将 m2 的输入放到 m1.m2input 上。

// m1.js
root.m2.m1input = this.value;
m2.update();

// m2.js
root.m1.m2input = this.value;
m1.update();

PS:不知道谁会这么写代码,除了我为了做演示之外。。。

查看完整代码demo

公共耦合

一组模块都访问同一个全局数据结构,则称之为公共耦合。

在这种 case 中,m1 和 m2 将自己的输入放到全局的 data 上。

// m1.js
root.data.m1input = this.value;
m2.update();

// m2.js
root.data.m2input = this.value;
m1.update();

查看完整代码demo

外部耦合

一组模块都访问同一全局简单变量,而且不通过参数表传递该全局变量的信息,则称之为外部耦合。外部耦合和公共耦合很像,区别就是一个是简单变量,一个是复杂数据结构。

在这种 case 中,m1 和 m2 都将自己的输入放到全局上。

// m1.js
root.m1input = this.value;
m2.update();

// m2.js
root.m2input = this.value;
m1.update();

查看完整代码demo

控制耦合

模块之间传递的不是数据信息,而是控制信息例如标志、开关量等,一个模块控制了另一个模块的功能。

从控制耦合开始,模块的数据就放在自己内部了,不同模块之间通过接口互相调用。

在这个 case 中,得增加一个需求,就是当 m1 的输入为空时,隐藏 m2 的显示信息。

// m1.js
root.m1input = this.value;
m2.update();

m2.toggle(!!this.value); // 传递flag

上面的代码中 m1 直接控制了 m2 的显示和隐藏。

查看完整代码demo

标记耦合

调用模块和被调用模块之间传递数据结构而不是简单数据,同时也称作特征耦合。

在这个 case 中,m1 传给 m2 的是一个对象。

// m1.js
me.m1input = this.value;
m2.update(me); // 传递引用

// m2.js
me.m2input = this.value;
m1.update(me);

查看完整代码demo

数据耦合

调用模块和被调用模块之间只传递简单的数据项参数。相当于高级语言中的值传递。

在这个 case 中,m1 传给 m2 的是一个简单数据结构。

// m1.js
me.m1input = this.value;
m2.update(me.m1input); // 传递值

// m2.js
me.m2input = this.value;
m1.update(me.m2input);

查看完整代码demo

非直接耦合

两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。

子模块无需知道对方的存在,子模块之间的联系,全部变成子模块和主模块之间的联系。

在这个 case 种,增加一个 index.js 作为主模块。

// index.js
var m1 = root.m1;
var m2 = root.m2;

m1.init(function (str) {
  m2.update(str);
});

m2.init(function (str) {
  m1.update(str);
});

// m1.js
me.m1input = this.value;
inputcb(me.m1input); // inputcb是回调函数

// m2.js
me.m2input = this.value;
inputcb(me.m2input);

查看完整代码demo

内聚

其实关于内聚也分为很多种,如下所示,如果你感兴趣可以自己研究研究,我们下次再来分享内聚的问题。

  • 偶然内聚
  • 逻辑内聚
  • 时间内聚
  • 通信内聚
  • 顺序内聚
  • 功能内聚

总结

希望你看完上面的文章,搞懂了耦合的种类,也希望你以后能使用非直接耦合这种方式来写代码,祝好。

原文网址:http://yanhaijing.com/program/2016/09/01/about-coupling/

微信公众号:颜海镜
关注微信公众号 颜海镜
微信支付二维码
赞赏支持 微信扫一扫


GitHub家园

GitHub家园 on 01 Sep 2016

图文并茂,一图胜千言

颜海镜

颜海镜 on 01 Sep 2016

新博文《详解7种耦合关系》,图文并茂,快来围观

manxisuo

manxisuo on 02 Sep 2016

别字:种 -> 中。

颜海镜

颜海镜 on 02 Sep 2016

感谢指出,求提个pr修改

前端-小强

前端-小强 on 02 Sep 2016

我就用pub/sub 来解耦了 晕

颜海镜

颜海镜 on 02 Sep 2016

这种方式也不错,我没有介绍,不过主模块的方式比pub sub还好,pub sub很容易导致消息爆炸,并没有解决耦合散落在各个模块的问题,如果是在业务中还可以用,如果是第三方模块,那就不能这么用了

前端-小强

前端-小强 on 02 Sep 2016

受教了! good

莫名小晟

莫名小晟 on 03 Sep 2016

言简意赅,明白了好多,谢谢

颜海镜

颜海镜 on 03 Sep 2016

加油

makuta

makuta on 05 Sep 2016

大神能讲下这几种耦合关系对应的实际应用场景吗? 嘻嘻

颜海镜

颜海镜 on 05 Sep 2016

不是有代码的demo吗

slogeor

slogeor on 18 Sep 2016

讲得不错,感觉区别不对。

颜海镜

颜海镜 on 18 Sep 2016

你好,哪里不对,欢迎指出

slogeor

slogeor on 21 Sep 2016

敲错了 衰 ,是感觉区别不大。如果能说各自的使用场景更好。

颜海镜

颜海镜 on 22 Sep 2016

demo里有举得例子

YuanChiFun

YuanChiFun on 02 Apr 2019

想看作者的内聚篇。。