重构第九章

         条件逻辑有可能十分复杂,因此这一章提供的重构手法专门用来简化它们。其中一项核心重构就是分解条件表达式,可以将一个复杂的条件逻辑分解成若干个小块,使得“分支逻辑”和“操作细节”分离。这一章的其他重构手法处理另一些重要的问题,比如代码中的多处测试有相同的结果应该实施“合并条件表达式”,如果代码中有任何重复可以运用“合并重复的条件片段”将重复部分去掉在面向对象的程序中如果出现switch语句,应该运用“以多态取代条件表达式将它替换”,多态还有一种鲜为人知的用途就是通过“引入null对象”去除对null值的检验。

9.1、分解条件表达式

什么时候用?

有一个复杂的条件(if-then-else)语句

怎么用?

从If、then、else三个段落中分别提炼出独立函数

具体步骤:

1、将if段落提炼出来,构成一个独立的函数

2、将then段落和else段落都提炼出来,各自构成一个独立的函数(如果发现嵌套的条件逻辑,先观察是否可以使用“以卫语句取代嵌套条件表达式”(此手法是下面的9.5),如果不行才开始分解其中的每个条件)

9.2、合并条件条件表达式

什么时候用?

有一列条件测试,都得到相同的结果

怎么用?

将这些测试合并为一个条件表达式,并将这个条件表达式提炼成为一个独立的函数

具体步骤:

1、确定这些条件语句都没有副作用(即最终行为一致)

2、使用适当的逻辑操作符,将一系列相关条件表达式合并为一个。然后编译测试

3、对合并后的条件表达式实施提炼方法

9.3、合并重复的条件片段

什么时候用?

在条件表达式的每一个分支上有着相同的一段代码

怎么用?

将这些代码搬至到条件表达式之外

具体步骤:

1、鉴别出“执行方式不随条件变化而变化”的代码

2、如果这些共通代码位于条件表达式起始处,就将它移到条件表达式之前

3、如果这些共通代码位于条件表达式尾端,就将它移到条件表达式之后

4、如果这些共通代码位于条件表达式中段,就需要观察共通代码之前或之后的代码是否改变了什么东西。如果有改变应该将共通代码向前或向后移动,移至条件表达式的起始处或者尾端,再以前面的步骤1、2、3处理。

5、如果共通代码不止一条语句,应该首先使用“提炼函数”将共通提炼到一个独立的函数中,再用前面的4、1、2、3步骤处理

9.4、移除控制标志

什么时候用?

在一系列布尔表达式中,某个变量带有“控制标记”的作用

怎么用?

以break语句或return语句取代控制标记

具体步骤:

对控制标记的处理,最显而易见的办法就是使用Java提供的break语句或continue语句。

1、找出跳出这段逻辑的控制标记值

2、找出对标记变量赋值的语句,代以恰当的break语句或continue语句

3、每次替换后,编译并测试

在未能提供break和continue语句的编程语言中,可以使用下述办法(即使在支持break和continue语句的编程语言中,通常也优先考虑下面的方案)

4、运用提炼函数,将整段逻辑提炼到一个独立函数中

5、找出跳出这段逻辑的控制标记值

6、找出对标记变量赋值的语句,代以恰当的return语句

7、每次替换后,编译并测试

9.5、以卫语句取代嵌套条件表达式

什么时候用?

函数中的条件逻辑使人难以看清正常的执行路径

怎么用?

使用卫语句表现所有特殊情况

具体步骤:

卫语句:如果两条分支都是正常行为,就应该使用形如if…else…的条件表达式,如果某个条件极其罕见就应该单独检查条件,并在该条件为真时立刻从函数中返回。

1、对于每个检查,放进一个卫语句(卫语句要不就从函数中返回,要不就抛出一个异常)

2、每次将条件检查替换成卫语句后,编译并测试(如果所有卫语句都导致相同的结果,使用合并条件表达式)

9.6、以多态取代条件表达式

什么时候用?

有个条件表达式,根据对象类型的不同而选择不同的行为。

怎么用?

将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。

具体步骤:

1、如果要处理的条件表达式是一个更大函数中的一部分,首先对条件表达式进行分析,然后使用提炼函数将它提炼到一个独立函数中

2、如果有必要,使用搬移函数将条件表达式放着到继承结构的顶端

3、任选一个子类,在其中建立一个函数,使之覆写超类中容纳条件表达式的那个函数。将于该子类相关的条件表达式分支复制到新建函数中,并对它进行适当调整。(为了顺利进行这一步骤,可能需要将超类中的某些private字段声明为protected),编译并进行测试

4、在超类中删掉条件表达式内被复制了的分支然后编译并测试

5、针对条件表达式的每个分支,重复上述过程,直到所有分支都被移到子类内的函数为止

6、将超类之中容纳条件表达式的函数声明为抽象函数

9.7、引入Null对象

什么时候用?

需要再三检查某对象是否为null

怎么用?

将null值转换为null对象

具体步骤:

1、为源类建立一个子类,使其行为就像是源类的null版本。在源类和null子类中都加上isNull()函数,前者的isNull()应该返回false,后者的isNull()应该返回true。然后编译

2、找出所有“索求源对象却获得一个null”的地方,修改这些地方,使他们改而获取一个空对象。

3、找出所有“将源对象与null比较”的地方。修改这些地方,使它们调用isNull()函数。编译并测试。

4、找出这样的程序点:如果对象不是null,做A动作,否则做B动作

5、对于每一个上述点:在null类中覆写A动作,使其行为和B动作相同

6、使用上述被覆写的动作,然后删除“对象是否等于null”的条件测试。编译并测试。

9.8、引入断言

什么时候用?

某一段代码需要对程序状态做出某种假设

怎么用?

以断言明确表现这种假设

具体步骤:

如果程序员不犯错,断言就应该不会对系统运行造成任何影响,所以加入断言永远不会影响程序的行为。

如果发现代码假设某个条件始终为真,就加入一个断言明确说明这种情况(可以新建一个assert类,用于处理各种情况下的断言)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值