怎么把if-else重构成高质量代码

技术怎么把if-else重构成高质量代码本篇内容介绍了“怎么把if-else重构成高质量代码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅

本文介绍了“如何将if-else重构为高质量代码”的知识。很多人在实际案例的操作中会遇到这样的困难。让边肖带领你学习如何处理这些情况。希望大家认真阅读,学点东西!

00-1010程序员一定经历过这样的场景:一开始自己写的代码非常简洁,逻辑清晰,功能简洁,没有if-else。随着代码逻辑的不断完善和业务的快速变化,比如:需要判断输入的类型和值;这里,判断对象是否为空;或者没有。不同的类型执行不同的过程。

If-else只能添加到具体的实现中。渐渐地,代码越来越大,功能越来越长,文件行数迅速超过几千行,维护难度越来越大。后期基本达到难以维持的状态。

虽然我们都不愿意写满屏的if-else代码,但是逻辑上需要特别的判断,也很绝望,但是没有办法避免。

实际上,回顾自己的代码,编写if-else无非是两种场景:异常逻辑处理和不同状态处理。

它们之间的主要区别在于,异常逻辑处理意味着只有一个分支是正常进程,而所有分支都是处于不同状态处理的正常进程。搜索程序员白楠楠微信官方账号,送你一本Java面试题宝典。

如何理解?例如:

//示例1:异常逻辑处理示例。

object obj=GetObj();

如果(obj!=null){ 0

//dosomething

}else{

//dosomething

}

//示例2:状态处理示例。

object obj=GetObj();

if(obj . GetType==1){ 0

//dosomething

} else if(obj . GetType==2){ 0

//dosomething

}else{

//dosomething

}第一个例子if (obj!=null)是异常处理和代码健壮性判断。只有当是正常处理流程,否则分支为错误处理流程。但是,第二个例子属于正常的业务流程,不管类型是等于1、2还是其他条件。对于这两种情况,重建方法是不同的。

过多的if-else代码有什么缺点?

缺点很明显:最大的问题是代码逻辑复杂,可维护性差,容易引起bug。如果使用If-else,则意味着if分支和else分支同等重视,但大多数情况下并非如此,容易导致误解和理解困难。

有没有好的优化方法?如何重建?

一定有办法的。在重构if-else时,我们应该始终把握一个原则:

尽量将正常的流代码保持在最外层。

也就是说,当您可以编写if-else语句时,您必须尝试将主代码保持为正常流程,并避免过度嵌套。

实现方法有:减少嵌套、去除临时变量、否定条件判断、合并条件表达式等。

下面是一些例子来解释这些重建方法:

重建前00-1010:

double disableyamount(){ 0

if(_seniority2)

返回0;

if(_monthsDisabled12)

返回0;

if(_isPartTime)

返回0;

//dosomeig

}重建后:

double disableyamount(){ 0

if(_ seniority 2 | | _ monthsdisabled 12 | | _ isPartTime)

sp;  return 0;
    //do somethig
}

这里的重构手法叫合并条件表达式:如果有一系列条件测试都得到相同结果,将这些结果测试合并为一个条件表达式。

这个重构手法简单易懂,带来的效果也非常明显,能有效地较少if语句,减少代码量逻辑上也更加易懂。

异常逻辑处理型重构方法实例二

重构前:

 double getPayAmount(){
     double result;
     if(_isDead) {
         result = deadAmount();
     }else{
         if(_isSeparated){
             result = separatedAmount();
        }
         else{
            if(_isRetired){
                result = retiredAmount();
            else{
                result = normalPayAmount();
            }
        }
    }
    return result;
}

重构后:

 double getPayAmount(){
     if(_isDead)
         return deadAmount();
 
     if(_isSeparated)
        return separatedAmount();
 
     if(_isRetired)
         return retiredAmount();
    return normalPayAmount();
}

怎么样?比对两个版本,会发现重构后的版本逻辑清晰,简洁易懂。

和重构前到底有什么区别呢?

最大的区别是减少 if-else 嵌套。可以看到,最初的版本 if-else 最深的嵌套有三层,看上去逻辑分支非常多,进到里面基本都要被绕晕。其实,仔细想想嵌套内的 if-else 和最外层并没有关联性的,完全可以提取最顶层。

改为平行关系,而非包含关系,if-else 数量没有变化,但是逻辑清晰明了,一目了然。

另一个重构点是废除了 result 临时变量,直接 return 返回。好处也显而易见直接结束流程,缩短异常分支流程。原来的做法先赋值给 result 最后统一 return,那么对于最后 return 的值到底是那个函数返回的结果不明确,增加了一层理解难度。

总结重构的要点:如果 if-else 嵌套没有关联性,直接提取到第一层,一定要避免逻辑嵌套太深。尽量减少临时变量改用 return 直接返回。

异常逻辑处理型重构方法实例三

重构前:

public double getAdjustedCapital(){
    double result = 0.0;
    if(_capital > 0.0 ){
        if(_intRate > 0 && _duration >0){
            resutl = (_income / _duration) *ADJ_FACTOR;
        }
    }
    return result;
}

第一步,运用第一招,减少嵌套和移除临时变量:

public double getAdjustedCapital(){
    if(_capital <= 0.0 ){
        return 0.0;
    }
    if(_intRate > 0 && _duration >0){
        return (_income / _duration) *ADJ_FACTOR;
    }
    return 0.0;
}

这样重构后,还不够,因为主要的语句 (_income / _duration) *ADJ_FACTOR; 在 if 内部,并非在最外层,根据优化原则(尽可能地维持正常流程代码在最外层),可以再继续重构:

 public double getAdjustedCapital(){
     if(_capital <= 0.0 ){
         return 0.0;
     }
     if(_intRate <= 0 || _duration <= 0){
         return 0.0;
     }
 
     return (_income / _duration) *ADJ_FACTOR;
}

这才是好的代码风格,逻辑清晰,一目了然,没有 if-else 嵌套难以理解的流程。

这里用到的重构方法是:将条件反转使异常情况先退出,让正常流程维持在主干流程。

异常逻辑处理型重构方法实例四

重构前:

    /* 查找年龄大于18岁且为男性的学生列表 */
     public ArrayList<Student> getStudents(int uid){
         ArrayList<Student> result = new ArrayList<Student>();
         Student stu = getStudentByUid(uid);
         if (stu != null) {
             Teacher teacher = stu.getTeacher();
             if(teacher != null){
                 ArrayList<Student> students = teacher.getStudents();
                 if(students != null){
                    for(Student student : students){
                        if(student.getAge() > = 18 && student.getGender() == MALE){
                            result.add(student);
                        }
                    }
                }else {
                    logger.error("获取学生列表失败");
                }
            }else {
                logger.error("获取老师信息失败");
            }
        } else {
            logger.error("获取学生信息失败");
        }
        return result;
    }

典型的"箭头型"代码,最大的问题是嵌套过深,解决方法是异常条件先退出,保持主干流程是核心流程:

重构后:

    /* 查找年龄大于18岁且为男性的学生列表 */
     public ArrayList<Student> getStudents(int uid){
         ArrayList<Student> result = new ArrayList<Student>();
         Student stu = getStudentByUid(uid);
         if (stu == null) {
             logger.error("获取学生信息失败");
             return result;
         }
 
        Teacher teacher = stu.getTeacher();
        if(teacher == null){
            logger.error("获取老师信息失败");
            return result;
        }
        ArrayList<Student> students = teacher.getStudents();
        if(students == null){
            logger.error("获取学生列表失败");
            return result;
        }
        for(Student student : students){
            if(student.getAge() > 18 && student.getGender() == MALE){
                result.add(student);
            }
        }
        return result;
    }

状态处理型重构方法实例一

重构前:

 double getPayAmount(){
     Object obj = getObj();
     double money = 0;
     if (obj.getType == 1) {
         ObjectA objA = obj.getObjectA();
         money = objA.getMoney()*obj.getNormalMoneryA();
    }
     else if (obj.getType == 2) {
         ObjectB objB = obj.getObjectB();
        money = objB.getMoney()*obj.getNormalMoneryB()+1000;
    }
}

重构后:

 double getPayAmount(){
     Object obj = getObj();
     if (obj.getType == 1) {
         return getType1Money(obj);
     }
     else if (obj.getType == 2) {
         return getType2Money(obj);
     }
 }
double getType1Money(Object obj){
    ObjectA objA = obj.getObjectA();
    return objA.getMoney()*obj.getNormalMoneryA();
}
double getType2Money(Object obj){
    ObjectB objB = obj.getObjectB();
    return objB.getMoney()*obj.getNormalMoneryB()+1000;
}

这里使用的重构方法是:把 if-else 内的代码都封装成一个公共函数。函数的好处是屏蔽内部实现,缩短 if-else 分支的代码。代码结构和逻辑上清晰,能一下看出来每一个条件内做的功能。

状态处理型重构方法实例二

针对状态处理的代码,一种优雅的做法是用多态取代条件表达式(《重构》推荐做法)。

你手上有个条件表达式,它根据对象类型的不同而选择不同的行为。将这个表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。

重构前:

 double getSpeed(){
     switch(_type){
         case EUROPEAN:
            return getBaseSpeed();
         case AFRICAN:
             return getBaseSpeed()-getLoadFactor()*_numberOfCoconuts;
         case NORWEGIAN_BLUE:
             return (_isNailed)?0:getBaseSpeed(_voltage);
     }
}

重构后:

 class Bird{
     abstract double getSpeed();
 }
 
 class European extends Bird{
     double getSpeed(){
         return getBaseSpeed();
    }
 }
class African extends Bird{
    double getSpeed(){
        return getBaseSpeed()-getLoadFactor()*_numberOfCoconuts;
    }
}
class NorwegianBlue extends Bird{
   double getSpeed(){
        return (_isNailed)?0:getBaseSpeed(_voltage);
    }
}

可以看到,使用多态后直接没有了 if-else,但使用多态对原来代码修改过大,需要一番功夫才行。最好在设计之初就使用多态方式。

总结

if-else 代码是每一个程序员最容易写出的代码,同时也是最容易被写烂的代码,稍不注意,就产生一堆难以维护和逻辑混乱的代码。

针对条件型代码重构把握一个原则:

尽可能地维持正常流程代码在最外层,保持主干流程是正常核心流程。

为维持这个原则:合并条件表达式可以有效地减少if语句数目;减少嵌套能减少深层次逻辑;异常条件先退出自然而然主干流程就是正常流程。

针对状态处理型重构方法有两种:一种是把不同状态的操作封装成函数,简短 if-else 内代码行数;另一种是利用面向对象多态特性直接干掉了条件判断。

现在回头看看自己的代码,犯了哪些典型错误,赶紧运用这些重构方法重构代码吧!!

欢迎关注公众号:程序员白楠楠,领取一份300页pdf文档的Java核心知识点总结!

这些资料的内容都是面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。

“怎么把if-else重构成高质量代码”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/40193.html

(0)

相关推荐

  • vue echarts使用总结

    技术vue echarts使用总结 vue echarts使用总结option = {tooltip: {//提示框组件,也就是我们平时经常看见的鼠标移入后会有一个框框,显示你当前移入的参数名称和数据t

    礼包 2021年11月4日
  • vue 组件对外暴露方法(vue 中的store如何存取数据)

    技术Vue中怎样把数据包装成reactive从而实现MDV效果Vue中怎样把数据包装成reactive从而实现MDV效果,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来

    攻略 2021年12月25日
  • 哪些水果含维生素c,含有维生素C的食物有什么

    技术哪些水果含维生素c,含有维生素C的食物有什么含维生素C的食物很多哪些水果含维生素c,好多水果蔬菜都有。水果比如苹果,梨,荔枝,橘子,葡萄等,蔬菜比如辣椒,白菜,茄子,番茄。好多好多,只要丰富点,换着吃,每天摄入量足够

    生活 2021年10月27日
  • jdk的内置命令是什么

    技术jdk的内置命令是什么本篇文章给大家分享的是有关jdk的内置命令是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。一、维护命令1.1 查看classe

    攻略 2021年11月11日
  • html5map标签的用法(html5 map标签)

    技术html5中map标签怎么用小编给大家分享一下html5中map标签怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

    攻略 2021年12月17日
  • XSS常见payload指的是什么

    技术XSS常见payload指的是什么这篇文章将为大家详细讲解有关XSS常见payload指的是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。环境:http://xs

    攻略 2021年12月9日