JS程序设计的常用套路
阿里妹导读
类型定义的套路
例子
...if (group === 'customer_free_group') {list.push({id: 'customer_free_cid',Type: 'EQ',bizType: 'customer_free',});} else if (group === 'customer_biz_group') {list.push({id: 'customer_biz_cid',Type: 'AND',bizType: 'customer_biz'});} else if (group === 'contact_group') {list.push({id: 'contact_cid',Type: 'IN',bizType: 'contact',});}...
使用枚举类型来定义这里的常量,类型定义一出来,仿佛代码逻辑就容易懂了一些:
enum GroupTypes {customerFreeGroup = 'customer_free_group', // 免费客户组customerBizGroup = 'customer_biz_group', // 收费客户组contactGroup = 'contact_group', // 联系人组}enum LogicalOperationTypes {EQ = 'EQ',IN = 'IN',AND = 'AND',GT = 'GT',LT = 'LT',}enum BizTypes {customer_free = 'customer_free', // 免费客户customer_biz = 'customer_biz', // 收费客户contact = 'contact', // 联系人}
...if (group === GroupTypes.customer_free_group) {list.push({id: IdTypes.customer_free_cid,type: LogicalOperationTypes.EQ,bizType: BizTypes.customer_free,});} else if (group === GroupTypes.customer_biz_group) {list.push({id: IdTypes.customer_biz_cid,type: LogicalOperationTypes.AND,bizType: BizTypes.customer_biz,});} else if (group === GroupTypes.contact_group) {list.push({id: IdTypes.contact_cid,type: LogicalOperationTypes.IN,bizType: BizTypes.contact,});}...
接着,我们发现代码里出现了多次相似的代码结构。相似代码消除也有很多套路,使用合适的类型定义也是其中的一个。当我们在代码中觉察到相似的结构时,可能可以动一动映射表(Map)的心思了。先尝试将相似的代码结构定义到一个映射表里:
const groupConfigMap = {customer_free_group: {id: IdTypes.customer_free_cid,type: LogicalOperationTypes.EQ,bizType: BizTypes.customer_free,},customer_biz_group:{id: IdTypes.customer_biz_cid,type: LogicalOperationTypes.AND,bizType: BizTypes.customer_biz,},contact_group: {id: IdTypes.contact_cid,type: LogicalOperationTypes.IN,bizType: BizTypes.contact,},};
定义好之后,接下来,主代码中的逻辑就会变成下面这样:
...if (group in groupConfigMap) {list.push(groupConfigMap[group]);}...
当group在映射表中时,直接将对应的对象推入list数组。这使得代码更简洁且易于扩展。如果未来有新的group类型,只需添加到映射表即可。主代码逻辑部分从20行缩减到3行。虽然一目十行要求有些高,但一眼看过去三行应该是没问题。变身后的代码一目了然,简单清晰,读起来感觉真不错。
小结
函数提取的套路
例子
...if (bizStatus === 'RUNNING') {bizName = 'flow_in_approval';} else if (bizStatus === 'COMPLETED') {if (bizAction === 'modify') {bizName = 'flow_modify';} else if (bizAction === 'revoke') {bizName = 'flow_revoke';} else {bizName = 'flow_completed';}} else {bizName = 'sw_flow_forward';}...
这段代码是通过BizStatus和BizAction的值来判断设置对应的bizName,逻辑相对独立,可以将其从几百行的原函数中抽出来,封装成一个新的函数getBizName。先用前面数据类型的套路整理一下,使用枚举来存放常量,并应用到代码中:
const BizStatus = {RUNNING: 'RUNNING',COMPLETED: 'COMPLETED',};const BizAction = {MODIFY: 'modify',REVOKE: 'revoke',REFUSE: 'refuse'};const getBizName = (bizStatus:BizStatus, bizAction:BizAction)=>{let bizName = '';if (bizStatus === BizStatus.RUNNING) {bizName = 'flow_in_approval';} else if (bizStatus === BizStatus.COMPLETED) {if (bizAction === BizAction.MODIFY) {bizName = 'flow_modify';} else if (bizAction === BizAction.REVOKE) {bizName = 'flow_revoke';} else {bizName = 'flow_completed';}} else {bizName = 'flow_forward';}return bizName;}
之后,原函数的位置变为了顺序结构。顺序结构是让人理解起来最轻松的一种结构了:
...bizName = getBizName(bizStatus, bizAction);...
小结
分支处理的套路
例子
const getBizName = (bizStatus, bizAction) => {if (bizStatus === BizStatus.RUNNING) {return 'flow_in_approval';}if (bizStatus === BizStatus.COMPLETED && bizAction === BizAction.MODIFY) {return 'flow_modify';}if (bizStatus === BizStatus.COMPLETED && bizAction === BizAction.REVOKE) {return 'flow_revoke';}if (bizStatus === BizStatus.COMPLETED) {return 'flow_completed';}return 'flow_forward';};
小结
变更封装的套路
例子
interface ITaskModel {key: string;iconUrl: string;title: string;subTitle: IDescriptionModel[];desc: IDescriptionModel[];status: ITaskStatusEnum;action: ITaskActionModel;utParams: IUTParamsModel;}interface ITaskActionModel {type: ITaskActionComponentType;name: string;targetConfig: IActionTargetModel;}interface IActionTargetModel {type: ITaskActionType;value: string;targetConfig: IActionTargetModel;}
最后就是数据驱动的逻辑实现了。
小结
未完待续