你在工作中是否也遇到这样的问题
也许很多同学对重构有很多误解,在读这本书之前,先用两个简单的例子快速的理解为什么重构有用。
1.长函数难以理解
敲代码的时候,难免会遇到很长的函数。长函数是让代码变得难以理解的最常见的问题之一。特别是那些没有任何注释,且命名又不规范的长函数里,你修改一点代码的都让你胆颤心惊。
2.长函数难以测试
长函数不仅难以理解,而且难以测试。测试长函数需要很多先决条件,而有的时候,仅仅需要测试其中的某一段代码块。
3.长函数带来重复代码
长函数的另一个罪恶是,它必然带来重复的代码,因为长函数里的代码块无法复用。当你需要用到长函数里的代码块,但是你又不想去动到长函数,可能最妥协的办法就是复制这一块代码。
重复的代码几乎是最可怕的事情,一句话就是:复制一时爽,修改火葬场。如果修改了复制的代码,你是无法确定到底全部修改完毕了没有。最后可能导致这个修改过的代码不生效,bug重复出现等问题!
4.短函数帮助你找出BUG
直接测试短函数,可以快速的定位问题。
在代码编辑器的帮助下,把长函数里面的代码块提取出来却是非常简单的事情。
在Idea下,只要选中需要被提取大代码块,按下Ctrl+Alt+M
如果有四两拨千斤的编码技巧的话,那么提取方法绝对是其中之一。简单的使用提取方法的重构手段,就能够让代码容易理解、修改,让你编码的速度飞快的提升。
1.switch导致代码耦合
switch是导致代码耦合的典型原因。在部分语言里,如python,都取消了这个关键字。
大部分情况下switch都会和type一起使用。
//一些前置的共同使用的代码
switch(type){
case type1:
//type1相关的代码
break;
case type2:
//type2相关的代码
break;
}
问题的关键在于,大部分情况下,type1和type2的代码是不相关的。但是仅因为type1需要去修改前置的共同使用的代码时,你不得不考虑这个修改会不会导致type2发生改变。如果你的type有很多,这种代码的耦合性导致修改代码时要考虑的情况程指数级上升。
2.switch导致重复代码
另一个使用switch的问题在于,一旦你使用了一个switch,你在其他的方法里不得不使用另一个switch,最后导致switch泛滥。当需要添加一个新的type时,你就知道这是一件多么恐怖的事情。举一个获取工资的例子:
//获取平常的工资
public double getSalary(String type){
switch(type){
case "普通员工":
//计算普通员工资
break;
case "管理层":
//计算管理层工资
break;
}
}
//获取节假日加班工资
public double getFestivalSalary(String type){
switch(type){
case "普通员工":
//计算普通员节假日工资
break;
case "管理层":
//计算管理层节假日工资
break;
}
}
那么既然不建议使用switch,有什么更好的方法吗?
是的,使用多态,我第一次看到这种重构方法,令我心头一惊,居然还有这种操作!为什么不早点告诉我!
public interface Staff{
double getSalary();
double getFestivalSalary();
}
public class GeneralStaff implements Staff{
public double getSalary(){
//计算并返回普通员工的平日工资
}
public double getFestivalSalary(){
//计算并返回普通员工的节假日工资
}
}
public class Management implements Staff{
public double getSalary(){
//计算并返回管理层的平日工资
}
public double getFestivalSalary(){
//计算并返回管理层的节假日工资
}
}
public class SalaryInfo {
private Staff _staff;
public void set_staff(Staff _staff) {
this._staff = _staff;
}
SalaryInfo(String type){
switch (type) {
case "普通员工":
set_staff(new GeneralStaff());
break;
case "管理层":
set_staff(new Management());
break;
}
}
//假设需要获取节假日工资+平日工资
public double getTotalSalary(){
return _staff.getSalary()+_staff.getFestivalSalary();
}
}
你也许会发现,重构后的代码还是有switch。但是这跟之前的switch不一样了,这是唯一的一个switch。
甚至跟进一步,这里的switch可以修改为反射,但是为了不让代码更复杂,我们在这里不进行进一步的优化。
另外的两个好处则非常明显。
通过这个重构,可以把混成一团的代码解耦开来。在这样的代码里新加功能,修改代码,难道不会比在混成一团的代码了快上10倍吗?
通过上面的两个例子,你应该了解了重构并不是什么类似架构师的高手的操作,他非常简单而且非常有效。甚至,你不需要征求任何人的同意去重构代码。只要重构代码能够让你工作更快且不会引起其他严重的问题,你就应该去重构。
再回到这本书《重构,改善既有代码的设计》。抽取函数和用多态替代switch只是重构的两个技巧。是否还有其他类似的四两拨千斤的技巧呢?
是的,都在这本书里了。但是这类技术书籍都是列表式的,无法通过像故事书那样通过概述让你了解大概。但是依然可以分为几个部分来进行简易的理解。
除了例子里展示的帮助你快速的修改代码和添加代码外,另一个重要的原因是:重构和预先设计是互补的。我们知道,在编码之前都会进行整体设计,详细设计等。但是我们也清楚,需求朝令夕改。而且新增的需求可能会完全超出原先设计的预想,所以完全按照最初的设计去编码,肯定是无法满足的,而重构则是补上预先设计无法满足的漏洞。通过根据最新的需求和代码情况进行代码的重构,保持代码的易维护易修改。如果没有引入重构,可以想象代码会像野草一样增长,最后到达无法理解的地步。而重构就像剪刀,对代码进行微小的修剪。
另一个令人困扰的问题是,何时进行重构。毕竟,攻城狮们都很忙,几乎不会有时间专门去重构。而且重构一些可能之后不会使用到的代码,反而没有什么价值。那什么情况下的重构下才有价值。
重构遇到的第一个困难是:如果重构没有目标,那么你可能会无休止的重构下去,导致最后改动非常大。所以我们需要清楚的知道,哪些代码是确实需要被重构的,哪些代码可能不需要重构。
在书里面列出了22种“代码的坏味道”。这看起来很多,幸运的是,我们并不需要考试,需要把这22种情况都记忆下来。
但是我们可以看看其中比较经典的“坏味道”。
尽管我们已经尽量在本文中详细的介绍该书中的很多细节。但是技术类的书籍大部分列表式的。本书的展示形式就是提供一个列表。每个列表就是一个重构的技法。包括
只要快速的过一遍所有重构技法,你不需要记住所有的操作。当你遇到类似的问题时,再回来查看相关的重构的步骤。也许效率会高一些。
综上,这本书真的可以让你快上10倍。如果想要获得这本书的话,关注公众号,回复重构获取下载链接!
这里放公众号二维码。