经过上面的重构步骤,我们会运行一下测试用例或者执行一下单元测试,看是否我们的重构过程引起了新的bug。
三、重构2:将相应的方法移到相应的类中
经过上面的重构,我们从statement()函数中提取了两个方法。观察这两个重构后的方法我们不难看出,这两个封装出来的新的方法都需要一个参 数,这个参数就是Rental类的对象。也就是这两个方法都依赖于Rental类,而对该函数所在的当前类不太感冒。出现这种情况的原因就是这两个函数放 错了地方,因为这两个函数放在Customer类中不依赖与Customer类而依赖于Rental类,那就足以说明这两个方法应该放在Rental类 中。
经过我们简单的分析后,我们就可以决定要将我们新提取的方法放到Rental类中,并且函数的参数去掉。因为函数在Rental类中,所以在函数中 直接使用self即可。将计算金额的方法和计算积分的方法移到Rental类中后,我们的Rental类如下所示。在我们的Customer中的 statement()方法中在计算金额和计算积分时,直接调用Rental中的方法即可。经过这一步重构后,不要忘记执行一下你的测试用例,监测一下重 构的结果是否正确。
四、使用“以查询取代临时变量”再次对statement()函数进行重构
经过第二步和第三步的重构后,Customer中的statement()函数如下所示。在计算每部电影的金额和积分时,我们调用的是Rental类的对象的相应的方法。下方的方法与我们第一部分的方法相比可谓是简洁了许多,而且易于理解与维护。
不过上面的代码仍然有重构的空间,举个例子,如果我们要将结果以HTML的形式进行组织的话,我们需要将上面的代码进行复制,然后修改result 变量的文本组织方式即可。但是这样的话,其中的好多临时变量也需要被复制一份,这是完全相同的,这样就容易产生重复的代码。在这种情况下,我们需要使用“Replace Temp with Query”(已查询取代临时变量)的重构手法来取出上面红框中的临时变量。
上面红框中的每个临时变量我们都会提取出一个查询方法,下方是使用“Replace Temp with Query”(已查询取代临时变量)规则重构后的statement()函数,以及提取的两个查询函数。
经过上面这些步骤的重构,我们的测试用例依然不变。在每次重构后我们都需要调用上述的测试用例来检查重构是否产生了副作用。现在我们的类间的依赖关 系没怎么发生变化,只是相应类中的方法有些变化。下方是现在代码所对应的类图,因为在上述重构的过程中我们主要做的是对函数的重构,也就是对函数进行提 取,然后将提取的函数放到相应的类中,从下方的简化的类图中就可以看出来了。
五. 继续将相应的函数进行移动(Move Method)
对重构后的代码进行观察与分析,我们任然发现在Rental类中的getCharge()函数中的内容与 getFrequentRenterPoints()函数中的内容对Movie类的依赖度更大。因为这两个函数都只用到了Rental类中的 daysRented属性,而多次用到了Movie中的内容。因此我们需要将这两个函数中的内容移到Movie类中更为合适。所以我继续讲该部分内容进行 移动。
移动的方法是保留Rental中这两个函数的声明,在Movie中创建相应的函数,将函数的内容移到Movie中后,再Rental中调用Movie中的方法。下方是我们经过这次重构后我们Movie类中的内容。其中红框中的内容是我们移过来的内容,而绿框中的参数需要从外界传入。
将相应的方法体移动Movie类中后,在Rental中我们需要对其进行调用。在调用相应的方法时传入相应的参数即可。下方就是经过这次中国Rental类的代码,绿框中的代码就是对Movie中新添加的方法的调用。
经过上面的重构,我们的方法似乎是找到了归宿了。重构就是这样,一步步来,不要着急,没动一步总是要向着好的方向发展。如果你从第一部分中的代码重构到第五部分,似乎有些困难。经过上面这些间接的过程,感觉也是挺愉快的蛮。下方是经过我们这次重构的类图。
六、使用“多态”取代条件表达式
在我们之前的博客中对条件表达式进行重构时,提到了使用类的多态对条件表达式进行重构。接下来我们就要使用该规则对Movie类中的getCharge()与getFrequentRenterPoints()函数进行重构。也就是使用我们设计模式中经常使用的“状态模式”。在该部分我们不需要对Rental类和Customer类进行修改,只对Movie类修改,并且引入相应的接口和继承关系。
我们对Movie类中的getCharge()方法中的Switch-Case结构观察时,我们很容易发现,此处完全可以使用类的多态来替代(具体请参见《代码重构(四):条件表达式重构规则(Swift版)》)。具体实现方式是将不通的价格计算方式提取到我们新创建的价格类中,每种电影都有自己价格类,而这些价格类都实现同一个接口,这样一来在Movie中就可以使用多态来获取价格了。积分的计算也是一样的。下方是我们要实现结构的类图。下方红框中是在原来基础上添加的新的接口和类,将条件表达式所处理的业务逻辑放在了我们新添加的类中。这样我们就可以使用类的多态了,而且遵循了“单一职责”。
下方代码就是上面大的红框中所对应的代码实现。Price是我们定义好的协议,在协议中规定了遵循该协议的类要实现的方法。而在每个具体实现类中实 现了相同的接口,但是不同的类中相同的方法做的事情不同。在不同的类中的getCharge()中要做的事情就是Switch-Case语句中所处理的数 据。
添加上上面的结构以后,在么我们的Movie中就可以使用多态了,在Movie中添加了一个Price声明的对象,我们会根据不同的 priceCode来给price变量分配不同的对象。而在getCharge()中只管调用price的getCharge()函数即可,具体做法如 下。
今天的博客到这儿也就差不多了,其实上面的代码仍然有重构的空间,如果我们想把Switch-Case这个结构去掉的话,我们可以在上面代码的基础上创建多个工厂方法即可。在此就不过赘述了。
如果看完今天的博客的内容不够直观的话,那么请放心。本篇博客中每次重构过程的完整实例会在github上进行分享。对每次重构的代码都进行了系统的整理。今天博客中的代码整理的结果如下。
github分享地址为:https://github.com/lizelu/CodeRefactoring-Swift
优质内容筛选与推荐>>
1、Spring Cloud Eureka服务Demo级搭建2、Lisp笔记13、2016 hosts4、window有哪些属性?5、Spring Boot Shiro权限管理2