阿里Java开发手册思考(二)

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

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

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

第一大类:从新组织函数

一、Extract Method(提炼函数)

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

  • 原始代码:
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.");
}
复制代码

用法:算法

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

二、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 "";
}
复制代码

用法:并发

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

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

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

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

  • 原始代码:
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段落都提炼出来,各自构成一个独立函数
  • 适用场景:复杂的条件语句。若是发现嵌套的条件逻辑,先观察是否可使用卫语句,若是不行,再开始分解其中的每一个条件

二、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();
}
复制代码

用法:

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

三、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();
}
复制代码

用法:

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

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

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

五、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;
}
复制代码

用法:

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

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

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

第三大类:简化函数调用

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

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

  • 并发的状况:须要保留第三个函数来同时作这两件事

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

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

  • 原始代码:
public void tenPercentRaise() {
	salary *= 1.1;
}

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

三、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;
}
复制代码

用法:

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

微信公众号: 码上论剑
请关注个人我的技术微信公众号,订阅更多内容
相关文章
相关标签/搜索