look-up表代替if-else
比如某平台的信用分数评级:
超过700-950,信用极好,
650-700信用优秀,
600-650信用良好,
550-600信用中等,
350-550信用较差。
常规写法:
function showGrace(grace) {
let _level='';
if(grace>=700){
_level='信用极好'
}
else if(grace>=650){
_level='信用优秀'
}
else if(grace>=600){
_level='信用良好'
}
else if(grace>=550){
_level='信用中等'
}
else{
_level='信用较差'
}
return _level;
}
看看运行也没问题,但是问题也是有的比如:
1- 万一以后需求,改了比如650-750是信用优秀,750-950是信用极好。这样就整个方法要改。
2- 方法存在各种神仙数字:700,650,600,550。日后的维护可能存在问题。
3- if-else太多,看着有点强迫症
用look-up表,把配数据置和业务逻辑分离的方式实现下
function showGrace(grace) {
let graceForLevel=[700,650,600,550];
let levelText=['信用极好','信用优秀','信用良好','信用中等','信用较差'];
for(let i=0;i<graceForLevel.length;i++){
if(grace>=graceForLevel[i]){
return levelText[i];
}
}
//如果不存在,那么就是分数很低,返回最后一个
return levelText[levelText.length-1];
}
这样的修改,优点就是如果有需求修改,只需要修改graceForLevel,levelText。业务逻辑不需要改。
为什么这里推荐配数据置和业务逻辑分离
1.修改配置数据比业务逻辑修改成本更小,风险更低
2.配置数据来源和修改都可以很灵活
3.荐配置和业务逻辑分离,可以更快的找到需要修改的代码
如果还想灵活一些,可以封装一个稍微通用一点的look-up函数。
通用一点的look-up函数
function showGrace(grace,level,levelForGrace) {
for(let i=0;i<level.length;i++){
if(grace>=level[i]){
return levelForGrace[i];
}
}
//如果不存在,那么就是分数很低,返回最后一个
return levelForGrace[levelForGrace.length-1];
}
let graceForLevel=[700,650,600,550];
let levelText=['信用极好','信用优秀','信用良好','信用中等','信用较差'];
第二个实例:
比如输入一个景点,给出景点所在的城市。
最low的办法:
function getCityForScenic(scenic) {
let _city=''
if(scenic==='广州塔'){
_city='广州'
}
else if(scenic==='西湖'){
_city='杭州'
}
return _city;
}
次low的办法:
function getCityForScenic(scenic) {
let _city='';
let scenicOfHangZhou=['西湖','湘湖','砂之船生活广场','京杭大运河','南宋御街'];
if(scenic==='广州塔'||scenic==='花城广场'||scenic==='白云山'){
_city='广州'
}
else if(~scenicOfHangZhou.indexOf(scenic)){
_city='杭州'
}
return _city;
}
次次low的办法: (采用 switch case)
function getCityForScenic(scenic) {
let _city='';
let scenicOfHangZhou=['西湖','湘湖','砂之船生活广场','京杭大运河','南宋御街'];
switch(true){
case (scenic==='广州塔'||scenic==='花城广场'||scenic==='白云山'):_city='广州';break;
case (!!~scenicOfHangZhou.indexOf(scenic)):return '杭州';
}
return _city;
}
虽然上面的代码出现的概率很小,但毕竟会出现。这样的代码可能会造成日后维看得眼花缭乱。如果使用了配置数据和业务逻辑分离,那就可以避免这个问题。
配置数据和业务逻辑分离
function getCityForScenic(scenic) {
let cityConfig={
'广州塔':'广州',
'花城广场':'广州',
'白云山':'广州',
'西湖':'杭州',
'湘湖':'杭州',
'京杭大运河':'杭州',
'砂之船生活广场':'杭州',
'南宋御街':'杭州',
}
return cityConfig[scenic];
}
不习惯对象的 key 名是中文。也可以灵活处理
function getCityForScenic(scenic) {
let cityConfig=[
{
scenic:'广州塔',
city:'广州'
},
{
scenic:'花城广场',
city:'广州'
},
{
scenic:'白云山',
city:'广州'
},
{
scenic:'西湖',
city:'杭州'
},
{
scenic:'湘湖',
city:'杭州'
},
{
scenic:'京杭大运河',
city:'杭州'
},
{
scenic:'砂之船生活广场',
city:'杭州'
}
]
for(let i=0;i<cityConfig.length;i++){
if(cityConfig[i].scenic===scenic){
return cityConfig[i].city
}
}
}
这里简单总结下,使用配置数据和业务逻辑分离的形式,好处
1- 修改配置数据比业务逻辑修改成本更小,风险更低
2- 配置数据来源和修改都可以很灵活
3- 配置和业务逻辑分离,可以更快的找到需要修改的代码
4- 配置数据和业务逻辑可以让代码风格统一
但是并不是所有的if-else都建议这样改造,有些需求不建议使用look-up改造。比如if-else不是很多,if判断的逻辑不统一的使用,还是建议使用if-else方式实现。但是神仙数字,要清除。
配置对象代替switch
比如有一个需求:传入cash,check,draft,zfb,wx_pay,对应输出:现金,支票,汇票,支付宝,微信支付。
需求也很简单,就一个switch就搞定了
function getPayChanne(tag){
switch(tag){
case 'cash':return '现金';
case 'check':return '支票';
case 'draft':return '汇票';
case 'zfb':return '支付宝';
case 'wx_pay':return '微信支付';
}
}
但是这个的硬伤还是和上面一样,万一下次又要多加一个如:bank_trans对应输出银行转账呢,代码又要改。类似的问题,同样的解决方案,配置数据和业务逻辑分离。代码如下。
function getPayChanne(tag){
let payChanneForChinese = {
'cash': '现金',
'check': '支票',
'draft': '汇票',
'zfb': '支付宝',
'wx_pay': '微信支付',
};
return payChanneForChinese[tag];
}
同理,如果想封装一个通用的,也可以的
let payChanneForChinese = {
'cash': '现金',
'check': '支票',
'draft': '汇票',
'zfb': '支付宝',
'wx_pay': '微信支付',
};
function getPayChanne(tag,chineseConfig){
return chineseConfig[tag];
}
getPayChanne('cash',payChanneForChinese);
总结:
在特定场合下,代替if-else和switch的解决方案就是这么多了。if-else,switch本身没错,主要是想着怎么优化代码,让代码更加具有可读性,扩展性。