重构-改善既有代码的设计
何为重构:在对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
为何重构:代码结构的流失是有累积性的,如果开始就很难看出代码所表达的设计意图,那么后续的人就越难保护好其中的设计,就会导致越改越腐 败的结果。所以,重构不仅可以让代码维持自己该有的形态和行为,也能让代码结构更加易于扩展,更加易于阅读维护。
第一章 简介
1.变量名:通用易懂,符合常用规定,便于阅读;任何傻瓜都能写出计算机可以理解的代码,但是能被人容易理解的代码才是优秀程序员的代码
2.消除重复代码
3.去除临时变量
4.方法抽取与内敛等
5.尽可能简单的表达条件逻辑
6.运用多态取代针对对象属性的不同逻辑,而非大量的if或者switch判断;
a.状态模式;视类为某种状态
b.策略模式;视类为某种方式
6. 编码中的常见方法措施:(不限以下几种)
提取方法;
移动方法;
用多态替换条件;
封装域;
用状态/策略替换类型代码;
第二章 重构原则
1.使软件更容易理解:代码的两个读者:电脑和人。
2.提高编程的速度:良好的设计是快速开发的根本。
3.事不过三,三则重构
4.保留旧接口:类似Java的“已弃用”;
第三章 代码的坏味道(哪些代码可以重构)
1.重复代码:
a.同一个类的两个函数含有相同的表达式(抽取出来)
b.两个互为兄弟的子类内含有相同的表达式(可以存入超类)
2.过长函数:对象拥有短函数会活的更好更久;程序越长越难理解;
3.过大的类:可以将之分解
4.过长参数列:过多的参数难以理解,可以提取到另一个独立的对象之中;
5.过多的switch:面向对象的特征就该少用switch。可用多态!当然某个单一函数用多态就有点杀鸡用牛刀!
6.令人迷惑的暂时字段:不要定义未使用的暂时参数,增加阅读迷惑性.
7.过度耦合的消息链条:a.b.c.d这样的方法请求形式。
8.过多的注释:注释不能少,但是不能用来解释一段质量糟糕的代码!而是尽可能去优化糟糕的代码,将其做到容易理解!
第四节:重新组织数据
4.1 自封装字段
为这个字段建立取值/设值函数,并且只以这些函数来访问字段;
以下面两段差异的代码作为比较:
// 直接使用数据
private int low,high;
boolean includes(int arg){
return arg >= low && arg <= hign;
}
// 间接使用数据
private int low,high;
boolean includes(int arg){
return arg >= low && arg <= hign;
}
int getLow(){return low;}
int getHigh(){return high;}
上面两种方法没有谁对谁错之分,要看使用的场景谁更合适;间接访问变量的好处,子类可以通过覆写一个函数而改变获取数据的途径,可以更灵活的进行数据管理;
一旦拥有一个子类,上述所有动作的价值就可以提现出来:
class CappedRange extents IntRange{
CappedRange (int low,int high,int cap){
super(low,high);
this.cap=cap;
}
private int cap;
int getCap(){
return cap;
}
int getHigh(){
return Math.min(super.getHigh(),getCap());
}
}
4.2 将值对象改为引用对象
场景:系统中有好很多地方引用某个对象,单这个对象保存少量不可修改的数据,而后,你想给这个对象加一些可修改的数据,并确保对任何一个对象的修改都能影响到所有引用此对象的地方。这时就需要将这个对象变成一个引用对象。(单例模式的场景)
4.3 以字面常量取代魔法数
一个包含业务意义的魔法数,如果没有说明业务意思,维护的人会一脸懵逼,为了方便理解,可以将其定义为有字面意义的常量;
// 男性
private static final String BOY = 1;
// 女性
private static final String GIRL = 0;
第五节:简化条件表达式
程序之中,复杂的条件表达式会使代码的复杂度上升,使其可读性下降。
5.1 分解条件表达式
如果一个条件表达式又长又难理解,我们可以将其分解为多个独立函数,并将原函数中对应的代码改为调用新建函数,从而更清澈的表达自己的意图。
// 分解前的示例
if(条件1 && 条件2 || 条件3){
}else{
}
// 分解后的示例
if(this.check()){
this.methodA();
}else{
this.methodB();
}
/**
* 添加详细的业务功能说明
*/
private Boolean check(){
return 条件1 && 条件2 || 条件3;
}
5.2 合并条件表达式
// 合并前的示例
if(num < 20 ) return 0;
if(age> 100 ) return 0;
if(isBoy) return 0;
// 合并后的示例
if(this.check(1,1,true)){
}
/**
* 条件合并到方法中
*/
private Boolean check(int num,int age,boolean isBoy){
if((num < 20) || (age> 100) || isBoy) return 0;
}
5.3 以卫语句取代嵌套添加表达式
/**
* 查询对应征单的主键id以及审批流程节点
* @param levyOrderExamines
* @return
*/
private List<LevyOrderExamineDto> getLevyOrderApplyId(List<LevyOrderExamineDto> levyOrderExamines){
List<LevyOrderExamineDto> hasOrderApplyIdList = new ArrayList<>(levyOrderExamines.size());
// 卫语句直接判断为空的分支情况
if(CollectionUtils.isEmpty(levyOrderExamines)){
return hasOrderApplyIdList;
}
List<String> levyOrderNos = new ArrayList<>(levyOrderExamines.size());
levyOrderExamines.forEach(item -> {
if (!levyOrderNos.contains(item.getLevyOrderNo())){
levyOrderNos.add(item.getLevyOrderNo());
}
});
if(CollectionUtils.isNotEmpty(levyOrderNos)){
List<LevyOrderExamineDto> withApplyIdDataList = levyOrderApplyMapper.getLevyOrderApplyId(levyOrderNos);
if(CollectionUtils.isNotEmpty(withApplyIdDataList)){
hasOrderApplyIdList.addAll(withApplyIdDataList);
}
}
return hasOrderApplyIdList;
}
其实,重构的知识点远不止以上举例子的这些,这些只是小的技巧方法,但是这样做可以提高代码的可阅读和可维护性;正所谓“代码一时爽,维护火葬场”,易于阅读和理解的代码才是优质的代码,那些为了炫技写的深奥复杂,难以阅读的代码并不值得学习。