函数式编程对象Validation

   

     Validation用于收集错误信息,Either会在遇到第一个错误后就返回了,而Validation会继续执行直到收集完所有的错误信息。

Validation适用于需要收集表单的所有错误的情形。来看一下官方写法:

const R= require('ramda');
const {validation} = require('folktale');
const {Success,Failure} = validation;



const cNameReg = /^[\u4E00-\u9FA5]/;
const eNameReg = /^[a-zA-Z\/ ]{2,20}$/;
const vEmpty = v => v.length !== 0 ? Success(v) : Failure([{emptyError:'客户姓名为必填项!'}]);
const vMin = v => v.replace(/\./g,'').length >= 2 ? Success(v) : Failure([{minError:'客户姓名长度需要在2-15个字符之间! '}])
const vMax = v => v.length <= 15 ? Success(v) : Failure([{maxError:'客户姓名长度需要在2-15个字符之间! '}])
const vChinese = v => cNameReg.test(v.trim()) ? Success(v) : Failure(([{chinsesError:'客户姓名不可包含英文、数字以及特殊字符'}]))

const isValidated = () => 'success'
const validated = R.curryN(4, isValidated);
const validateForm = (name)=> 
            Success(validated)
                .ap(vEmpty(name))
                .ap(vMin(name))
                .ap(vMax(name))
                .ap(vChinese(name))

const result = validateForm('').value;
const err = result.value === 'success' ? '' : R.reduce(R.merge,{},result.value);//将数组转换为键值对,方便你更新到redux或vuex的状态
console.log(err);

 

   用了curryN创造了一个接受4个参数的柯里化函数,只有当它被传入第4个参数时才会执行(因为当它被传入第四个参数时,表示验证成功了)。

   注意其中的ap方法,当容器中包裹的是一个函数时,可以调用容器的ap方法执行该函数。ap的参数是一个被包裹的值。总之就是,

ap可以帮你执行放在容器里的方法。一般ap的实现是:

ap:function(f){

   return f.map(this.value);

}

   为什么上面的代码可行?这就必须要了解validation的ap方法,Success的ap会正常的执行自己包裹的方法,而Failure的ap不会执行方法,不仅如此,它还会判断参数是否也是Failure类型,如果是,它将会把参数的value和自己的value合并起来,这也是为什么我们要给Failure的构造函数传入数组参数的原因。

   但是,但是,对于一个不了解validation对象的人(他本来也无需了解)来说,这样的代码还比不上普通的流程式代码易懂,尤其是其中那个curryN,当然还有ap。阅读源码我们发现validation对象还有一个concat方法,它可以将错误结果连接起来,若成功它会返回原值。这样调用:

const name = '';
const result = vEmpty(name)
            .concat(vMin(name))
            .concat(vMax(name))
            .concat(vChinese(name))
const errMsg = result instanceof Success ? '' : R.reduce(R.merge,{},result.value);
console.log(errMsg);

可以把这种调用抽象成一个工具方法叫validate

const validate = (...params)=> R.reduce(R.concat,Success(''),params)
const name = '';
const result = validate(vEmpty(name),vMin(name),vMax(name),vChinese(name));
const errMsg = result instanceof Success ? '' : R.reduce(R.merge,{},result.value);
console.log(errMsg);

把获得errMsg的代码也放到validate里面

const validate = (...params)=> {
    let result = R.reduce(R.concat,Success(''),params);
    return result instanceof Success ? '' : R.reduce(R.merge,{},result.value)
}

把validate提取到工具库中,最终的代码如下:

const R = require('ramda');
const {validation} = require('folktale');
const {Success,Failure} = validation;
const {validate} = require('./ramda_utils')


const cNameReg = /^[\u4E00-\u9FA5]/;
const eNameReg = /^[a-zA-Z\/ ]{2,20}$/;
const vEmpty = v => v.length !== 0 ? Success(v) : Failure([{emptyError:'客户姓名为必填项!'}]);
const vMin = v => v.replace(/\./g,'').length >= 2 ? Success(v) : Failure([{minError:'客户姓名长度需要在2-15个字符之间! '}])
const vMax = v => v.length <= 15 ? Success(v) : Failure([{maxError:'客户姓名长度需要在2-15个字符之间! '}])
const vChinese = v => cNameReg.test(v.trim()) ? Success(v) : Failure(([{chinsesError:'客户姓名不可包含英文、数字以及特殊字符'}]))

const result = validate(vEmpty(name),vMin(name),vMax(name),vChinese(name));
console.log(result);

   没法更简洁了吗?当然可以,例如将验证方法中的三元表达式写法改为函数式写法,但这样代码虽短了,可读性也变差了,所以我们不做。再比如validate方法的参数是四个验证函数的调用结果,传入了四个name,完全可以用Ramda的converge合并这四个调用,例如这样写

const getVaidateMonad = R.converge((...params) => params,[vEmpty,vMin,vMax,vChinese]);
const monads = getVaidateMonad(name);
const result = validate(...monads);

   但这样写又能比上面好到哪里去呢?我宁愿用易读的可能啰嗦的代码,也不要用这种高冷的搞脑子的代码。

   另外,多次传递name还有这样一个原因,我们假使要验证多个元素呢?那么我们有的验证方法传入的就是不同的值了,这种写法正是可以兼容这种情况。

   我们还可以把验证方法写成和前面介绍的Either时那样,验证方法只有逻辑判断,而不包含返回Success或Failure对象的代码,这样能得到更精简的验证方法。然而现实对追求完美的人是很残酷的,假使你这样做了,你仍然必须提供报错信息的数组,那validate方法就变成了需要接受两个数组作为参数,一个是方法数组,一个是错误信息数组。这么做的唯一好处竟只是不在验证方法中提到函数对象,吃那么多的苦,却只学这么少的乖,我们不干。

   (以上代码都经过测试,直接可用。)下一节想讲一下IO。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值