一、读书心得
1.提炼函数
含义:代码可以被组织在一起并独立出来。
动机:颗粒度更小,易于复用。
做法:1.创造新函数,根据函数的意图命名函数;2.将需要提炼的代码复制到新函数中;3.检查代码是否引用“作用于限于源函数”的变量;4.检查是否有“仅用于被提炼代码段”的临时变量,如有,在目标函数中将它们生命为临时变量;5.检查是否有任何局部变量的值被改变;6.将被提炼代码段中需要读取的局部变量当作参数传递给目标;6.在被提炼函数原来位置调用新函数;7.编译,测试;
2.内联函数
含义:在函数调用的地方插入函数本体。
动机:函数简洁易懂,非必要的间接层让人看着不舒服。
做法:1.检查函数确定其不具有多态性;2.找出该函数所有被调用点,替换为函数本体;3.编译测试;4.删除该函数定义。
3.提炼变量
含义:增加解释性的变量声明。
动机:更宽的范围可以使用到这个变量,意味着其他代码也可以使用这个表达式,而不用再写一遍。
做法:1.确认提炼的表达式没有副作用;2.声明一个不可修改的变量;3.用新变量取代原来的表达式;4.测试
4.内联变量
含义:内联临时变量。
动机:临时变量妨碍了重构附近的代码。
做法:1.确认变量赋值语句的右侧表达式没有副作用;2.将其变为不可修改并测试;3.找到第一处使用该变量的地方将其替换为直接使用赋值语句的右侧表达式。4.测试;5.重复前面两步;6.删除变量的声明点和赋值语句;7.测试
5.改变函数声明
含义:根据情景修改函数名,或添加移除参数。
动机:更清晰表达用意。
做法:1.移除参数前确认函数体内没有用到该参数;2.修改函数声明;3.用新声明替换所有旧的声明;4.测试
6.封装变量
含义:以函数的形式封装对该数据的所有访问。
动机:缩小可访问范围。
做法:1.创建封装函数,在函数内部操作变量;2.执行静态检查;3.检查所有使用该变量的地方,确认后替换;4.执行测试。
7.变量改名
含义:修改变量名。
动机:是程序更易阅读。
做法:1.如果变量被广泛使用,考虑封装变量;2.新变量名替换所有老变量名;3.测试。
8.引入参数对象
含义:将一组经常一起出现的多个变量组合成对象作为一个参数。
动机:为了让数间的关系更加清晰,使用新的数据结构。
做法:1.创建新的数据结构;2.在原来的基础上加入新的参数并测试;3.逐一用新的参数替代之前的参数项。
9.函数组合成类
含义:将一组多次出现的函数封装进一个类。
动机:对象内部调用这些函数可以少传许多参数,从而简化函数调用。
做法:1.对多个函数共用的数据进行封装;2.对于使用数据的每个函数,都移到新类中;3.用于处理数据的逻辑可以提炼成函数并移入新类。
二、代码实例
@Override
public List<PaymentChannelView> consultWithSimulateAndUserBalance(ChannelConsultDTO request) {
List<PaymentOptionConfView> list = paymentConsultDomainService.consultWithSimulateAndUserBalance(request);
List<PaymentChannelView> convertViews = PaymentConsultConvert.convert(list);
return convertViews;
}
public static List<PaymentChannelView> convert(List<PaymentOptionConfView> paymentOptionConfViews) {
if (CollectionUtils.isEmpty(paymentOptionConfViews)) {
return new ArrayList<>();
}
List<PaymentChannelView> paymentChannelViews = new ArrayList<>();
for (PaymentOptionConfView view : paymentOptionConfViews) {
paymentChannelViews.add(view.getPaymentChannelView());
}
return paymentChannelViews;
}
需要在paymentChannelViews对象中有很多封装对象,当需要往里面添加字段的时候要,要在其助攻找到需要添加的对象再对其进行修改,而getPaymentChannelView()是包含有业务参数的处理逻辑的,所以在不知情的情况下很容易草率的认为get方法只是简单的获取对象而已。
在看了《重构》之后,发现内联函数也是重构的做法之一,于是做了以下尝试:
public static List<PaymentChannelView> convert(List<PaymentOptionConfView> paymentOptionConfViews) {
if (CollectionUtils.isEmpty(paymentOptionConfViews)) {
return new ArrayList<>();
}
return setUpPaymentChannelViewList(paymentOptionConfViews);
}
public static List<PaymentChannelView> buildPaymentChannelView(List<PaymentOptionConfView> paymentOptionConfViews){
List<com.alibaba.global.fund.fincore.response.PaymentChannelView> paymentChannelViews = new ArrayList<>();
for (PaymentOptionConfView view : paymentOptionConfViews) {
paymentChannelViews.add(fillPaymentChannelView(view));
}
return paymentChannelViews;
}
public static PaymentChannelView fillPaymentChannelView(PaymentOptionConfView view){
PaymentChannelView channelView = new PaymentChannelView();
channelView.setActive(view.getActive());
channelView.setInactiveReason(view.getInactiveReason());
channelView.setChannelUserInfo(view.getChannelUserInfo());
return channelView;
}
重构代码把getPaymentChannelView()的实现放到了当前的类中,并把PaymentChannelViews的遍历提取出来,以达到提升代码可读性的效果。
三、总结
重构的意义在于提升代码的可读性、可维护性、可拓展性等,我们在日常工作中经常会碰到需要改造代码的需求,在其过程中都会对要改的代码有了深刻的认识。如果代码在阅读的过程中很容易让人感到迷惑,那么我认为这串代码就得值得考虑其设计是否合理,重构之后是否能比原来的可读性更强。