typescript函数式编程的一次实践
前提:为了培养团队的技术能力,拿了个小修改需求作为函数式编程的实践机会。
需求:业务部门提出积分活动,当用户达成任务时送积分。简化逻辑,我们暂且认为逢5送5分,逢10送10分,其他送1分。
即达成任务1-4次时,每次送1分。第5次送5分,第10次送10分。
实现过程:
mkdir [项目路劲]
npm init --y
npm install typescript ts-node --save-dev
//测试用
npm install jasmine @types/jasmine --save-dev
新建文件fpdemo.ts
为了实现函数式,先定义两个帮助方法:
1)等价于if/else的either
2)等价于for循环的repeat
either的目的是传入一个条件、左分支、右分支,根据条件决定执行左分支还是右分支。右分支还能嵌套执行either方法。
repeat代替for循环,目的是依次执行每个either方法。
全部代码实现如下
export namespace fpdemo {
//奖励积分常量
const FifthAward = 5;
const TenthAward = 10;
const OthersAward = 1;
//第五次
export const IsFiveTimes = (arg: number)=> arg % 5 === 0;
//第十次
export const IsTenTimes = (arg: number)=> arg % 10 === 0;
type eitherFn = <U, T>(value: U) => () => T;
//这里使用函数重载,right可以是一个数值,也可以嵌套的either方法
function either<U>(condition: (arg: U) => boolean, left: number, right: number): any
function either<U>(condition: (arg: U) => boolean, left: number, right: eitherFn): any
function either<U>(condition: (arg: U) => boolean, left: number, right: number|Function): any
{
return (value: U) => condition(value)? left :
typeof right === "number"? right : right(value);
}
//代替for循环
function repeat(current: number, cap: number, fn: Function, total=0): any {
total += fn(current);
return current>=cap? total : repeat(++current, cap, fn, total);
}
console.log(
//传入数值,判断奖励的数量
either(IsTenTimes, TenthAward, either(IsFiveTimes, FifthAward, OthersAward))(10)
);
//从1-10,累加奖励
console.log(repeat(1, 10, either(IsTenTimes, TenthAward, either(IsFiveTimes, FifthAward, OthersAward))));
}
ts-node fpdemo.ts
执行结果可以看到1-10次的累加总额。
说到这里,可能有人会有疑问,费了这么大的劲,就为了写个for+if/else就能解决的问题。 传统(junior)的写法,会这么做
let times = 10;
let totalAward = 0
for(let i=0; i<times; i++ )
{
if(i%10 == 0)
{
totalAward += 10;
}
else if(i%5 == 0)
{
totalAward += 5;
}
else {
totalAward += 1;
}
}
依我的理解,fp的好处至少有两点
1:可复用 - 代码里的每个function都可以单独导出,并在他处复用
2:可测试 - 由于没有外部依赖,函数都是幂等的,所以每个function都可以导出并单元测试
最后安装jasmine, 进行单元测试
添加配置文件jasmine.json
{
"spec_files": ["**/*[sS]pec.ts"]
}
新建文件fpdemo.spec.ts,代码如下
import * as demo from './fpdemo';
describe('five', function() {
it('add', function() {
let result = demo.fpdemo.IsFiveTimes(5);
expect(result).toEqual(true);
})
})
修改package.json, 加入测试脚本
"scripts": {
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json"
},
运行npm run test,可以看到测试结果