《重构》一本书告诉你,为什么有的程序员比你快10倍

你在工作中是否也遇到这样的问题

  • 接手老项目,发现老项目代码惨不忍睹,无从下手
  • 添加新功能,发现代码难以拓展,心里骂着mmp,谁写的代码,翻看记录发现时自己写的。
    然而此时,为什么有些同事代码却刷刷敲的飞快?
    嘘!我这有本秘籍《重构,改善既有代码的设计》,看完这本书,赶英超美不是梦~
    《重构》一本书告诉你,为什么有的程序员比你快10倍_第1张图片

也许很多同学对重构有很多误解,在读这本书之前,先用两个简单的例子快速的理解为什么重构有用。

把长函数提取成短函数

为什么要提取成短函数

1.长函数难以理解

敲代码的时候,难免会遇到很长的函数。长函数是让代码变得难以理解的最常见的问题之一。特别是那些没有任何注释,且命名又不规范的长函数里,你修改一点代码的都让你胆颤心惊。

2.长函数难以测试
长函数不仅难以理解,而且难以测试。测试长函数需要很多先决条件,而有的时候,仅仅需要测试其中的某一段代码块。

3.长函数带来重复代码

长函数的另一个罪恶是,它必然带来重复的代码,因为长函数里的代码块无法复用。当你需要用到长函数里的代码块,但是你又不想去动到长函数,可能最妥协的办法就是复制这一块代码。
重复的代码几乎是最可怕的事情,一句话就是:复制一时爽,修改火葬场。如果修改了复制的代码,你是无法确定到底全部修改完毕了没有。最后可能导致这个修改过的代码不生效,bug重复出现等问题!
《重构》一本书告诉你,为什么有的程序员比你快10倍_第2张图片

4.短函数帮助你找出BUG

直接测试短函数,可以快速的定位问题。

如何提取方法

在代码编辑器的帮助下,把长函数里面的代码块提取出来却是非常简单的事情。
在Idea下,只要选中需要被提取大代码块,按下Ctrl+Alt+M
《重构》一本书告诉你,为什么有的程序员比你快10倍_第3张图片

如果有四两拨千斤的编码技巧的话,那么提取方法绝对是其中之一。简单的使用提取方法的重构手段,就能够让代码容易理解、修改,让你编码的速度飞快的提升。

使用多态替代switch

switch的罪恶

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;
	}
}

《重构》一本书告诉你,为什么有的程序员比你快10倍_第4张图片

使用多态解耦

那么既然不建议使用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可以修改为反射,但是为了不让代码更复杂,我们在这里不进行进一步的优化。

另外的两个好处则非常明显。

  • 修改其中一个type的代码不会影响其他type。
  • 如果新增一个新的type将会非常简单,只需要一个实现接口的类并在唯一的switch增加相关代码就可以了。

通过这个重构,可以把混成一团的代码解耦开来。在这样的代码里新加功能,修改代码,难道不会比在混成一团的代码了快上10倍吗?

通过上面的两个例子,你应该了解了重构并不是什么类似架构师的高手的操作,他非常简单而且非常有效。甚至,你不需要征求任何人的同意去重构代码。只要重构代码能够让你工作更快且不会引起其他严重的问题,你就应该去重构。

《重构,改善既有代码的设计》

再回到这本书《重构,改善既有代码的设计》。抽取函数和用多态替代switch只是重构的两个技巧。是否还有其他类似的四两拨千斤的技巧呢?
是的,都在这本书里了。但是这类技术书籍都是列表式的,无法通过像故事书那样通过概述让你了解大概。但是依然可以分为几个部分来进行简易的理解。
《重构》一本书告诉你,为什么有的程序员比你快10倍_第5张图片

为什么要重构

除了例子里展示的帮助你快速的修改代码和添加代码外,另一个重要的原因是:重构和预先设计是互补的。我们知道,在编码之前都会进行整体设计,详细设计等。但是我们也清楚,需求朝令夕改。而且新增的需求可能会完全超出原先设计的预想,所以完全按照最初的设计去编码,肯定是无法满足的,而重构则是补上预先设计无法满足的漏洞。通过根据最新的需求和代码情况进行代码的重构,保持代码的易维护易修改。如果没有引入重构,可以想象代码会像野草一样增长,最后到达无法理解的地步。而重构就像剪刀,对代码进行微小的修剪。

何时进行重构

另一个令人困扰的问题是,何时进行重构。毕竟,攻城狮们都很忙,几乎不会有时间专门去重构。而且重构一些可能之后不会使用到的代码,反而没有什么价值。那什么情况下的重构下才有价值。

  • 当你修改BUG时
    当你修改代码,如果代码不容易理解,那么很可能修改的代码治标不治本,或者隐藏的问题无法发现。而且,难以理解的代码,测试也将变得非常困难。修改这样的代码,如果没有重构,那修改是否成功大部分情况下就只能拜八阿哥了。
  • 当你新增功能时
    当你新增功能,你发现很难进行拓展。比如大量的switch,那么使用多态进行重构,让代码容易被拓展。这不仅这一次让你快速的拓展功能,虽然重构会耗费一点时间,但是减少的调试bug的时间绝对会弥补回来的。
  • 何时不应该进行重构
    并不是所有的时刻都适合做代码重构,比如deadline已经逼近,这个适合还是暂时不重构了。

哪些代码需要被重构

重构遇到的第一个困难是:如果重构没有目标,那么你可能会无休止的重构下去,导致最后改动非常大。所以我们需要清楚的知道,哪些代码是确实需要被重构的,哪些代码可能不需要重构。
在书里面列出了22种“代码的坏味道”。这看起来很多,幸运的是,我们并不需要考试,需要把这22种情况都记忆下来。
但是我们可以看看其中比较经典的“坏味道”。

  • 重复代码
    重复的代码是最常见的问题,我们无法完全消除重复代码,但是应该尽量的避免代码重复。
  • 过长的函数
    这个我们已经在例子里阐述过了。
  • 过大的类
    如果不对类加以整理重构,那么随着代码的增加,类变得巨大几乎是不可避免的事情。过大的类负责太多的事务,往往会导致大量的代码耦合。遇到过大的类应该警惕,通重构的手段,诸如上文提到的使用多态等方法分离类的功能。
  • switch
    当遇到switch时,应当警惕,switch往往是配合type一起使用,而type随着时间不可避免的会增多,导致类变得复杂和耦合。
  • 散弹式修改
    如果当你修改一个功能点,发现需要在各个地方都进行修改。而且你还不能保证所有的点都被修改了。这时候应该考虑把相同的代码整合到一块,并通过调用来引用功能。

如何阅读《重构,改善既有代码的设计》

尽管我们已经尽量在本文中详细的介绍该书中的很多细节。但是技术类的书籍大部分列表式的。本书的展示形式就是提供一个列表。每个列表就是一个重构的技法。包括

  • 重构技法的名称
  • 重构的动机
  • 重构的步骤
  • 重构的例子

只要快速的过一遍所有重构技法,你不需要记住所有的操作。当你遇到类似的问题时,再回来查看相关的重构的步骤。也许效率会高一些。

总结

综上,这本书真的可以让你快上10倍。如果想要获得这本书的话,关注公众号,回复重构获取下载链接!

这里放公众号二维码。

你可能感兴趣的:(技术书籍阅读)