前言
公司最近在改造一个之前做好的产品级大项目。这个项目之前是用的Spring MVC + jsp做的。现在我们要把这个项目改成 Spring boot + Spring MVC + mybatis + react的架构。改造后的项目将按照业务分成十几个微服务,使用Spring cloud进行服务治理。说是改造但为了满足进度和工期,我们后台的做法是找到对应的模块代码迁移到新的微服务中。在这个过程中我们发现了很架构,性能,编码规范等一系列问题,于是我们这支追求卓越的开发团队一致要求加强过程中对代码的重构。
下面我将给出一些重构切入点以便团队参考。
重构前提
重构也是有前提的。重构就是对既有代码的修改,那么即有代码肯定有他原来的业务逻辑,我们进行重构时一定要满足原来的业务逻辑。那么,我们怎么保证这一点呢?那就是为即将修改的代码建立一组可靠的测试环境。这些测试是必要的,因为尽管遵循重构手法可以使我避免绝大多数引入bug的情形,但我毕竟不是神,还是有可能犯错。所以我需要可靠的测试环境。
在此推荐大家阅读并参照《重构改善即有代码的设计》,《阿里巴巴Java开发手册》等书目进行重构。
重构切入点
移除不必要的属性设值方法
如果你为某个字段提供了设置函数,这就暗示这个字段值可以被改变。如果你不希望在对象被创建之后此字段还有机会被改变,那就不要为它提供设值函数,同时将该字段设置为final。这样你的意图会更加清晰,并且可以排除其值被修改的可能性—-这种可能性往往是非常大的。
属性下放
超类中的某个字段只被部分(而非全部)子类用到。将这个字段移到需要它的那些子类去。
封装字段(Encapsulate Field)
你的类中存在一个public字段,将它声明为private,并提供相应的访问函数。
面向对象的首要原则之一就是封装,或者称为“数据封装”。按此原则你绝对不应该将数据声明为private,否则其他对象就有可能访问甚至修改修改这项数据,而拥有该数据的对象却毫无感觉。
通过这项重构手法,你可以将数据隐藏起来,并提供相应的访问函数。但它毕竟只是第一步。如果一个类除了访问函数不能提供其他行为,它终究只是一个哑巴类。这样的类不能享受对象技术带来的好处。而你知道,浪费任何一个对象都是很不好的。实施Encapsulate Field之后,我会尝试寻找到新建访问函数的代码,看看是否可以通过简单的Move Method轻快的将它们移动到新对象中。
分解并重组过长的方法
要知道,代码块越小,代码的功能就越容易管理,代码的处理和移动也就越轻松,可读性也会更强。对方法的行数进行限制也是很多编码规范中的要求。
分解并重组过长的方法三部曲:抽取方法、移动方法、使用多态。
目的是:对象方法责任合理分配、代码易于维护。
重命名方法名
函数的名称未能揭示函数的用途,则考虑对重命名方法名。
移除未使用的参数
程序员可能经常添加参数,却往往不愿意去掉它们。他们打的如意算盘是:无论如何,多余的参数不会引起任何问题,而且以后还可能用上它。
这是恶魔的诱惑,一定要把它从脑子里赶出去!参数代表着函数所需的信息,不同的参数代表不同的意义。函数调用者必须为每一个参数操心该传什么东西进去。如果你不去掉多余参数,就是让你的每一位用户多操一份心。是很不划算的。
方法下放
超类中的某个方法只被部分(而非全部)子类用到。将这个方法移到需要它的那些子类去。
方法隐藏
有一个函数,从来没有被其他任何类使用到,将这个函数设置为private。
重构往往促使你修改函数的可见度。
简化条件语句
简化条件语句(if else、switch case等),使得程序逻辑更加清楚,同时方便扩展。
做法
合并条件语句:能合则合
将多个条件合并,并以一个函数(Extract Method)的形式表示这个条件,即将复 杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
例:1
2
3
4
5
6
7// 伪代码如下
if ((file.open(fileName, "w") != null) && (...) || (...)) { ...
}
// 上面代码应该进行如下重构
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...); if (existed) {
... }合并重复的条件执行片段
重复的条件执行片段,可以提出到条件之外,根据情况提到条件之前或条件之后。- 用守卫语句代替嵌套条件:特别条件直接return
含义:守卫语句就是要么return要么抛异常的语句。
用法:守卫语句通常用在一些不。寻常的条件处,表示一旦发生直接返回。守卫语句可以减少很多if - then - else的跳转,使逻辑变得清晰明了。有的时候,为了使用守卫语句,需要将已有的条件逆转,在逆转条件的时候不要使用非操作,这样不直接,非操作的条件都可以改成反向的条件。 - 引入Null对象:Null对象也是对象,利用多态
注意:空对象一定是常量,它们的任何成分从来不发生变化。因此,我们实现空对象的时候使用单例模式。
做法:主要是考虑 NullObject和isNull方法。
提炼超类
重复代码是系统中最糟糕的东西之一。如果你在不同地方做同一件事,一旦需要修改那些代码,你就得平白做更多的修改。
重复代码的某种形式就是:
两个类以相同的方式做类似的事情,或者以不同的方式做类似的事情。对象提供了一种简化这种情况的机制,那就是继承。
另一种选择就是Extract Class,这两种方案之间的选择就是继承和委托之间的选择。
总结
万能切入点,看着不顺眼的地方。但记得前提是你有良好的编码习惯,可靠的测试环境和高覆盖率的自测。
重构的节奏:测试、小修改、测试、小修改、测试、小修改……正是这种节奏让重构得以快速而安全地前进。