上期我们分享了关于Java中equals与hashCode的理解

本期我们将分享Java中if/else复杂逻辑的处理

在github上曾看到一些issue,国外的程序员比较忌讳写else,看到了很多这样的评论else is horrible,那么对于逻辑很复杂的代码段,如果用太多的if/else的话,那么会导致代码的阅读难度变大,同时会增加代码的圈复杂度,理论上,如果一个函数的圈复杂度超过8,那么这个函数就还有可优化的地方,那么如何优化这种多分支的复杂逻辑的函数呢?手册中给出了三种方法:卫语句策略模式状态模式,通过阅读《重构:改善既有代码的设计》发现,解决这个问题其实有很多种,下面我们就一一道来。

第一大类:重新组织函数

1、Extract Method(提炼函数)

这种方法应该是最常用的方法之一,当函数过长或者分支太多的话,就可以考虑将其中的一段代码提炼成一个独立的函数。

  • 原始代码:
public void today() {if (isBusy()) {System.out.println("change time.");} else if (isFree()) {System.out.println("go to travel.");} else {System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");}
}
复制代码
  • 改后代码:
public void today() {if (isBusy()) {changeTime();} else if (isFree()) {goToTravel();} else {stayAtHomeToLearn();}
}private void changeTime() {System.out.println("change time.");
}private void goToTravel() {System.out.println("go to travel.");
}private void stayAtHomeToLearn() {System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
}
复制代码

用法:

  • 提炼新函数,根据这个函数的意图来命名,以它做什么来命名,而不是以他怎么做来命名
  • 仔细检查提炼出的代码是否引用了作用域限于源函数的变量,包括局部变量和源函数参数
  • 适用场景:函数过长或者需要注释才能让人理解用途的代码

2、Substitute Algorithm(替换算法)

把某个算法替换成另一个更清晰的算法,或将函数本体替换为另一个算法。

  • 原始代码:
public String foundPerson(String[] people) {for (int i = 0; i < people.length; i++) {if (people[i].equals("Don")) {return "Don";}if (people[i].equals("John")) {return "John";}if (people[i].equals("Kent")) {return "Kent";}}return "";
}
复制代码
  • 改后代码:
public String foundPerson(String[] people) {List<String> candidates = Arrays.asList(new String[] { "Don", "John", "Kent" });for (int i = 0; i < people.length; i++) {if (candidates.contains(people[i])) {return people[i];}}return "";
}
复制代码

用法:

  • 准备好另一个替换用的算法
  • 新算法,要与原本的算法结果相同
  • 适用场景:把某个算法替换为更清晰的算法,或者把函数替换为一个算法

第二大类:简化条件表达式

1、Eecompose Conditional(分解条件表达式)

如果有复杂的条件(if-then-else)语句,从if、then、else三个段落中分别提炼出独立函数。

  • 原始代码:
public void today() {if (isBusy() || isNotWeekend()) {System.out.println("change time.");return;} else {System.out.println("go to travel.");}
}
复制代码
  • 改后代码:
public void today() {if (notFree()) {changeTime();} else {goToTravel();}
}private boolean notFree() {return isBusy() || isNotWeekend();
}private void changeTime() {System.out.println("change time.");
}private void goToTravel() {System.out.println("go to travel.");
}
复制代码

用法:

  • 将if段落提炼出来,构成一个独立函数
  • 将then段落和else段落都提炼出来,各自构成一个独立函数
  • 适用场景:复杂的条件语句。如果发现嵌套的条件逻辑,先观察是否可以使用卫语句,如果不行,再开始分解其中的每个条件

2、Consolidate Conditioinal Expression(合并条件表达式)

如果有一系列条件测试,都得到相同结果,将这些测试合并为一个条件表达式,并将这个条件表达式提炼成为一个独立函数。

  • 原始代码:
public void today() {if (isWeekend()) {System.out.println("go to travel.");}if (isHoliday()) {System.out.println("go to travel.");}if (noWork()) {System.out.println("go to travel.");}
}
复制代码
  • 改后代码:
public void today() {if (isFree()) {System.out.println("go to travel.");}
}private boolean isFree() {return isWeekend() || isHoliday() || noWork();
}
复制代码

用法:

  • 确定这些条件语句都没有副作用
  • 使用适当的逻辑操作符,将一系列相关条件表达式合并为一个,并对合并后的表达式提炼函数
  • 适用场景:一系列条件测试,都得到相同的结果

3、Consolidate Duplicate Conditional Clauses(合并重复的条件判断)

在条件表达式的每个分支上有相同的一段代码,将这段代码搬移到条件表达式之外。

  • 原始代码:
public void today() {if (isBusy()) {System.out.println("change time.");sleep();} else if (isFree()) {System.out.println("go to travel.");sleep();} else {System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");sleep();}
}
复制代码
  • 改后代码:
public void today() {if (isBusy()) {System.out.println("change time.");} else if (isFree()) {System.out.println("go to travel.");} else {System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");}sleep();
}
复制代码

用法:

  • 鉴别出执行方式不随条件变化而变化的的代码
  • 如果这些共同代码位于条件表达式起始处,就将它移到条件表达式之前,如果在尾端,移到条件表达式之后
  • 适用场景:在条件表达式的每个分支上有相同的一段代码

4、Remove Control Flag(移除控制标记)

在一系列布尔表达式中,某个变量带有“控制标记(Flag)”的作用,以break语句或者return语句取代控制标记。

5、Replace Nested Confitional with Guard Clauses(以卫语句取代嵌套条件表达式)

如果多个分支都属于正常行为,就应该使用if...else...的条件表达式,如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回,这样的单独检查常常被称为卫语句。

  • 原始代码:
public void today() {if (isBusy()) {System.out.println("change time.");} else if (isFree()) {System.out.println("go to travel.");} else {System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");}
}
复制代码
  • 改后代码:
public void today() {if (isBusy()) {System.out.println("change time.");return;}if (isFree()) {System.out.println("go to travel.");return;}System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");return;
}
复制代码

用法:

  • 对于每个检查,放进一个卫语句,卫语句要么就从函数中返回,要么就抛出一个异常
  • 每次将条件检查替换成卫语句后,编译并测试:如果所有的卫语句都导致同样的结果,请使用合并条件表达式
  • 适用场景:使用卫语句返回所有特殊情况

6、Replace Conditional with Polymorphism(以多态取代条件表达式)

条件表达式,根据对象的类型选择不同的行为,将这个条件表达式的每个分支放进一个子内的覆写函数中,然后将原始函数声明为抽象函数,这一项就是手册中说的策略模式以及状态模式。 正因为有了多态,所以“类型码的switch语句”以及“基于类型名称的if-then-else”语句在面向对象程序中很少出现。

第三大类:简化函数调用

1、Separate Query from Modifier(将查询函数和修改函数分离)

某个函数既返回对象状态值,又修改了状态,建立两个不同的函数,其中一个负责查询,另一个负责修改。

  • 并发的情况:需要保留第三个函数来同时做这两件事

2、Parameterize Method(令函数携带参数)

若干函数做了类似的工作,但在函数本体中却包含了不同的值,建立单一函数,以参数表达那些不同的值。

  • 原始代码:
public void tenPercentRaise() {salary *= 1.1;
}public void fivePercentRaise() {salary *= 1.05;
}
复制代码
  • 改后代码:
public void raise(double factor) {salary *= (1 + factor);
}
复制代码
  • 要点在于:以可将少量数值视为参数为依据,找出带有重复性的代码

3、Replace Parameter with Explicit Methods(以明确函数取代参数)

有一个函数,其中完全取决于参数值而采取不同行为,针对该参数的每一个可能值,建立一个独立的函数。

  • 原始代码:
public void setValue(String name, int value) {if (name.equals("height")) {height = value;}if (name.equals("width")) {width = value;}
}
复制代码
  • 改后代码:
public void setHeight(int arg) {height = arg;
}public void setWidth(int arg) {width = arg;
}
复制代码

用法:

  • 针对参数的每一种可能值,新建一个明确的函数
  • 修改条件表达式的每个分支,使其调用合适的新函数
  • 适用场景:函数完全取决于参数值而采取不同行为

微信公众号:码上论剑 请关注我的个人技术微信公众号,订阅更多内容

阿里Java开发手册思考(二)相关推荐

  1. 阿里JAVA开发手册零度的思考理解(二)

    转载请注明原创出处,谢谢! 说在前面 人生的大道上默默地走,就必须要有一盏灯亮着为你引导方向!而这盏灯抑或只是一句话,一句鼓励,一个赞美,一次承认,一次认可,一次相识一次交流-- 上篇文章:阿里JAV ...

  2. 阿里 java 开发手册

    关闭 学习中 <a href="https://climberclimbing.github.io/">Climbercliming </a> 目录视图 摘 ...

  3. 阿里Java开发手册之编程规约

    阿里Java开发手册之编程规约 对于程序员来说,编程规范可以养成良好的编程习惯,提高代码质量,降低沟通成本.就在2月9号,阿里出了一份Java开发手册(正式版),分为编程规约,异常日志,MySQL规约 ...

  4. java m4a文件拼接_面试官:为啥不提倡字符串拼接?看阿里java开发手册怎么说

    阿里规约 先来看阿里java开发手册的第22条,循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展. 说明:下例中,反编译出的字节码文件显示每次循环都会 ne ...

  5. 在线阅读-阿里Java开发手册最强版本【泰山版】

    前言 <Java 开发手册>是阿里巴巴集团技术团队的集体智慧结晶和经验总结,经历了多次大规模一 线实战的检验及不断完善,公开到业界后,众多社区开发者踊跃参与,共同打磨完善,系统化地整理 成 ...

  6. 阿里JAVA开发手册(泰山版)

    目录 前言 一.编程规约 (一)命名风格 (二)常量定义 (三)代码格式 (四)OOP 规约 (五)日期时间 (六)集合处理 (七)并发处理 (八)控制语句 (九)注释规约 (十)其它 二.异常日志 ...

  7. Java 命名规范---阿里Java开发手册

    0)Service/DAO层方法命名规约 1)获取单个对象的方法用get做前缀. 2)获取多个对象的方法用list做前缀. 3)获取统计值的方法用count做前缀. 4)插入的方法用save(推荐)或 ...

  8. 常识之外的规范——阿里java开发手册笔记(全章节)

    说明 这篇文章是我第一次(认真)阅读<阿里巴巴 Java 开发手册(终极版)>的笔记.手册本身对规范的讲解已经非常详细了,如果你已经有一定的开发经验并且有良好的编码习惯和意识,会发现大部分 ...

  9. 阿里Java开发手册——如何优化数据库?

    作者:杨冠宝/高海慧 来自:码出高效 Java 开发手册 数据库作为服务器端最为最为昂贵的资源之一,如果使用不当常常会导致系统卡顿或系统崩溃,那如何来优化数据库呢?下面来看阿里巴巴<Java开发 ...

最新文章

  1. javascript设计模式学习日记--模板方法模式
  2. 广东移动数据中心攻关“液/气双通道散热技术”
  3. Pytorch学习(一)—— 自动求导机制
  4. Django学习~1
  5. spring异常 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderServlet
  6. Python CSV 中查找指定字符串
  7. 微信支付宝扫一扫进入小程序的相关配置
  8. OpenShift 4 - DevSecOps Workshop (6) - 为Pipeline增加SonarQube实现SAST
  9. 80×86汇编常用指令
  10. woocommerce产品选项描述修改_简历修改服务:中文修改、英文修改、中英互译、简历定制,名师一对一指导修改!...
  11. VS中的多线程(/MT)、多线程调试(/MTd)、多线程DLL(/MD)、多线程调试DLL(/MDd)的区别
  12. 如何去掉 Visual Studio源代码 出现 对齐的点点
  13. 【系统集成项目管理工程师】—三点估算
  14. HAL库中外设驱动的实现(任意外设通用)
  15. java.lang.NoClassDefFoundError异常原因分析和解决方案
  16. 庄子心得09:大道与自然
  17. Python中字母大小写转换
  18. ffmpeg 抽取视频原始编码数据(mpeg4)方法
  19. Android开发相关介绍--基础篇
  20. CS231n Module2: CNN part1:Architecture

热门文章

  1. HP服务器集成 iLO 端口的配置
  2. 解决zabbix可用性为灰色状态
  3. VBS操作Excel数据
  4. 【Spark】sparksql中使用自定义函数
  5. 【数据仓库】数据集市
  6. 【问题待解决】自定义控件设计界面报错,编译运行正常
  7. JSON跨域解决方案收集
  8. 记一次升级node版本后,运行原vue项目报错问题解决方法
  9. Git - 推送当前分支快捷方式
  10. 向pandas DataFrame添加一行