耦合

耦合

上一节我们重点关注了扇出的内容,以及用例子说明了如何才能将一个函数或者模块的代码通过重构来达到一个合理的扇出值。今天我们来理解耦合:

耦合是关注模块如何组合在一起的,增加子模块或许可以减少扇出的值,但是不能减少原始模块对最初依赖之间的耦合度;本质是将显示依赖变成了间接依赖。

虽然今天我们不讲解内聚,但是我们也说说什么是内聚,以及什么是耦合?

 

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

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

耦合一般有六个级别,它们分别是:

1、内容耦合

内容耦合是最紧的耦合,包括在外部对象上调用方法或者函数,或通过修改外部对象的属性直接修改对象的状态。以下是例子:

let O = function() {
}
O.property = 'blah'; //直接改变外部对象的属性
O.methodName = function(){} //改变外部对象的方法
O.prototype.methodName = function(){} //直接改变了对象的原型

以上与外部对象O之间都是一种内容耦合。

 

2、公共耦合

比内容耦合略低一点的就是公共耦合。如果两个对象都共享另外一个全局变量,那么这两个对象就有公共耦合了。

var Global = 'global';
Function A() { GLobal = 'A'; };
Function B() { GLobal = 'B'; };

在这里,对象A和对象B就是公共耦合。

 

3、控制耦合

控制耦合比公共耦合的耦合度低一些。该耦合基于标记或者参数设置来控制外部对象。举个例子,创建一个单例抽象工厂,接着传入一个env标记告知该抽象工厂如何操作。这就是一种控制耦合。

var absFactory = new AbstractFactory({ env: 'Test'});

 

4、印记耦合

印记耦合是通过向外部对象传递一个记录,而只用该记录的一部分。举个例子

O.makeBread({ type: wheat, size:99, name: 'foo'});
O.prototype.makeBread = function(args) {
	return new Bread(args.type. args.size);
}

以上代码中,向makeBread函数传递一个记录,而函数只使用了该记录三个属性中的两个,这就属于印记耦合。

 

5、数据耦合

耦合类型最松散的就是数据耦合。这种耦合发生在一个对象传递给另一个对象消息数据,而没有传递控制外部对象的消息参数。简单理解就是方法调用的时候传递数据,这些数据不能让被调用的方法具有分支逻辑啥的。

 

6、无耦合

最后一种耦合形式就是无耦合,两个对象之间的绝对零耦合。

 

我们用一个例子来说明减少耦合对于代码测试的好处:

function setTable() {
	var cloth = new TableCloth();
	var dishes = new Dishes();
	this.placeTableCloth(cloth);
	this.placeDishes(dishes);
}

我们看这段代码,setTable函数耦合了两个对象TableClothDishes,而我在测试这个方法的时候必须为这两个对象进行模拟,因此代码测试将变得困难。修改成一下依赖注入以后

function setTable(cloth, dishes) {
	this.placeTableCloth(cloth);
	this.placeDishes(dishes);
}

这样修改以后,我通过向函数注入对象,起到了将方法和对象隔离的作用,测试起来也会更加简单。

 

另一种减少耦合的方法就是通过工厂和抽象工厂设计模式来重构代码,感兴趣的可以去网上看看相关的内容。

如果大家对耦合如何用代码来表现感觉有疑惑的话,可以看看这篇博文

版权声明:著作权归作者所有。

有没有无耦合的例子以及数据耦合的例子呢?
thumb_up 0 | star_outline 0 | textsms 1