敲碎时间的人的个人专栏
上一篇

函数的命令和查询分裂

广告
选中文字可对指定文章内容进行评论啦,→和←可快速切换按钮,绿色背景文字可以点击查看评论额。
大纲

上一节,我们知道了什么是命令,以及什么是查询。

今天我们继续前面的例子,来试着将一个功能太多的函数进行命令和查询的分离。

 

 

 

 

 

 

/**
 * 查询函数(用于设置值并返回)
 *
 */	
function configure(values) {
	var config = { docRoot: '/somewhere' };
 	var key;
 	for (key in values) {
 		config[key] = values[key];
 	}
 	return config;
}

/**
 * 命令函数(用与验证config的docRoot)
 *
 */
function validateDocRoot(config) {
	var fs = require('fs');
	var stat;
 	stat = fs.statSync(config.docRoot);
	if (!stat.isDirectory()) {
		throw new Error('Is not valid');
 	}
}

/**
 * 命令函数(用于验证config的其他值)
 *
 */
function validateSomethingElse(config){ ... }

好了,这样我们就将一个复杂的configure()函数分离称为了三个简单的函数,且每个函数或者是命令函数或者是查询函数,这样会方便对每个函数进行测试。

注意:前面我们说过命令函数属于setter和查询函数属于getter,而新抽象出来的configure()函数即包含了对config对象的设置值,又返回了一个config对象,那么它到底是命令函数查询呢?

我的一个简单判断就是,因为这个函数有返回值,所以把它当做查询来进行测试

测试代码

有了命令和查询的分离以后,我就可以对这些简单的命令和查询函数进行测试了.

require("@fatso83/mini-mocha").install();
const sinon = require("sinon");
const { assert } = require("@sinonjs/referee");

var fs = require('fs');
/**
 * 命令函数(用与验证config的docRoot)
 *
 */
function validateDocRoot(config) {
	var stat;
 	stat = fs.statSync(config.docRoot);
	if (!stat.isDirectory()) {
		throw new Error('Is not valid');
 	}
}
describe('validate value1', ()=>{
	
	it('accept the correct value',function(){
		const config = validateDocRoot({docRoot: '/tmp'});
		assert.isUndefined(config);
	});
	it('accept the incorrect value',function(){
        var statSync= sinon.stub(fs , 'statSync').callsFake(()=>{
    	   return {
        	   isDirectory: function(){
            	   return false;
        	   }
    	   };
	   });
       const fn = function() {
            validateDocRoot({docRoot: '/xxx'});
       }
	   assert.exception(fn);
	});
});
//在这里例子中,我们对validateDocRoot函数进行了测试,
//注意一下,这里提取出validateDocRoot函数以后,要么验证正确,要么抛出错误信息
//与上一节中,抛出了错误还返回一个undefined这种处理方式,更加提供了更好可理解性
//同时这里使用了一个stub来模拟fs模块的功能,不需要nodejs环境就可以直接测试啦。
//以往的情况是必须安装nodejs,现在我们直接在浏览器上就可以直接搞定
function validateSomethingElse(){
}

这里我们的configure()有点小问题就是缺少了验证,因此需要重构一下:

/**
 * 此函数接收一个散列对象,要么返回一个有效的配置对象要么抛出一个错误
 *
 */
function configure(values) {
	var config = { docRoot: '/somewhere' };
 	var key;
 	for (key in values) {
 		config[key] = values[key];
 	}
 	validateDocRoot(config);
	validateSomethingElse(config);
 	return config;
}

顺便提一点,在上一个版本的验证函数中,我们看到如果抛出错误,函数返回的是一个undefined而有了命令和查询分离以后,这个返回undefined的奇怪逻辑也不需要了。

 

以上函数算是初步进行了分离,但是存在的一个问题是,configure()的测试需要依赖验证函数,可以尝试做一些抽象,比如:

var fields = {
 	docRoot: { validator: validateDocRoot, default: '/somewhere'},
	somethingElse: { validator: validateSomethingElse}
}
function configure(values) {
	var config = {};
	for (var key in fields) {
 		if (typeof values[key] !== 'undefined') {
			fields[key].validator(values[key]);
			config[key] = values[key];
		}else {
 			config[key] = fields[key].default;
 		}
 	}
	return config;
}

这个函数有几个优点,验证函数可以方便的测试,对象赋值的时候可以进行验证,且数据都在一个中央位置。

当然这个函数也存在缺陷,至于到底存在什么问题呢?我们下一节进行分析。

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