阿里妹导读
类型定义的套路
例子
...
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;
}
最后就是数据驱动的逻辑实现了。
小结
未完待续