推荐阅读:
-
在重构老项目的过程中,我们不难发现经常会有这样的代码:构造函数很多(个人认为超过2个以上的构造函数所属的类,就有必要进行一番修改了),往往不知道如何维护,遇到版本迭代的时候,如果要修改之前的老代码则显的信心不足,只能在里面加补丁,代码习惯好点的可能加补丁的时候还会增加一点注释,代码习惯差一点的注释都没有。时间久了这部分代码就成了屎山,而且这个类也变的越来越难以维护。本文就从几个维度上教你如何重构类似的场景。
下面是一个支付结果的类,看看类似的代码风格在你的项目中有没有(如果真的没有说明你们的团队技术水平真的很好,建议点击右上角关闭本文,不要浪费时间)
//支付渠道
interface PayChannel {
}
//银行渠道
class BankChannel implements PayChannel {
}
//微信
class WxChannel implements PayChannel {
}
//支付宝
class AliPayChannel implements PayChannel {
}
/**
* 支付结果类
*/
public class PayResult {
//支付渠道
private PayChannel payChannel;
//支付时间
private Date payDate;
//订单总金额
private Double totalValue;
//实际支付金额
private Double paymentValue;
//用券抵消的金额
private Double couponValue;
//贷款支付的金额
private Double loanValue;
//银联支付 没有 用券的资格 也没有用贷款支付的资格
public PayResult(Date payDate, Double totalValue, Double paymentValue) {
this.payDate = payDate;
this.totalValue = totalValue;
this.paymentValue = paymentValue;
this.payChannel = new BankChannel();
this.loanValue = 0.0d;
this.paymentValue = 0.0d;
}
//微信支付没有贷款支付的能力
public PayResult(Date payDate, Double totalValue, Double paymentValue, Double couponValue) {
this.payDate = payDate;
this.totalValue = totalValue;
this.paymentValue = paymentValue;
this.couponValue = couponValue;
this.payChannel = new WxChannel();
this.loanValue = 0.0d;
}
public PayResult(PayChannel payChannel, Date payDate, Double totalValue, Double paymentValue, Double couponValue, Double loanValue) {
this.payChannel = payChannel;
this.payDate = payDate;
this.totalValue = totalValue;
this.paymentValue = paymentValue;
this.couponValue = couponValue;
this.loanValue = loanValue;
}
}
可以看出来PayResult这个类 仅仅有六个字段,但是构造函数就达到了三个。很不好维护。而且通常在项目里,这样的基础类可能用到的地方很多很多,我们一时半会也不敢随意大改,怕引发线上故障。
image.png类似于:
public class TestMain {
public static void main(String[] args) {
PayResult p1 = new PayResult(new Date(), 3.1d, 3.1d);
PayResult p2 = new PayResult(new AliPayChannel(), new Date(), 5.2d, 3.2d, 3.1d, 0.1d);
PayResult p3 = new PayResult(new Date(), 3.1d, 2.1d, 1.0d);
}
}
调用的地方太多,压根不敢随便大改。毕竟重构的首要条件是不要引发线上故障。那么有没有较为温和的方式能够优化一下这样的代码呢? 毕竟其实上述的构造函数里面的重复代码也挺多的,构造函数越多,重复代码的伤害就越大。碰到这种情况,我们通常会利用构造函数链接来完成重构,就是指:特殊的构造函数会调用更通用的构造函数,直到到达最后一个构造函数。 讲白了,其实就是让多余的构造函数之间通过this.构造函数 来进行一个收敛,决绝重复代码,仅此而已。这样改起来,不会造成侵入性过大,维护成本也较小。
例如:
//银联支付 没有 用券的资格 也没有用贷款支付的资格
public PayResult(Date payDate, Double totalValue, Double paymentValue) {
this(new BankChannel(), payDate, totalValue, paymentValue, 0.0d, 0.0d);
}
//微信支付没有贷款支付的能力
public PayResult(Date payDate, Double totalValue, Double paymentValue, Double couponValue) {
this(new WxChannel(), payDate, totalValue, paymentValue, couponValue, 0.0d);
}
//这个就是全包构造函数了 构造函数最全的就可以称之为全包构造函数,当我们的构造函数过多的时候
//就可以将多余的构造函数都最终指向我们的全包构造函数,这是一个收敛的过程
public PayResult(PayChannel payChannel, Date payDate, Double totalValue, Double paymentValue, Double couponValue, Double loanValue) {
this.payChannel = payChannel;
this.payDate = payDate;
this.totalValue = totalValue;
this.paymentValue = paymentValue;
this.couponValue = couponValue;
this.loanValue = loanValue;
}
改完以后就很简单,我们将我们的构造函数进行了统一的收敛,这在业务负载,改动频繁的场景中会变的十分好用, 不会因为增加删除或者变更了某些字段而引发大面积改动。
如果是一个有代码洁癖的人,改到这里也会觉得不舒服,还想继续改。因为这样改完虽然能够部分解决构造函数复杂,维护成本巨大的问题,但是代码的可读性却没有改观。
对于构造函数来说,可读性非常非常重要。本质上来说,如果一个类的构造函数越多,那么程序员犯错的可能性就越高,因为你构造函数太多了,多到你的构造函数本身无法有效和高效的表达你的意图。
而且对于很多长年累月的老项目来说,很多构造函数甚至都过时了,根本没人用。但是因为可读性的问题,敢删掉他们的人不多。
下面继续介绍一种方法,看看能不能在改动不大的情况下,重构我们的代码,把可读性给提高一下。
//银联支付
public static PayResult createUnionPayResult(Date payDate, Double totalValue, Double paymentValue) {
return new PayResult(new BankChannel(), payDate, totalValue, paymentValue, 0.0d, 0.0d);
}
//微信支付
public static PayResult createWxPayResult(Date payDate, Double totalValue, Double paymentValue, Double couponValue) {
return new PayResult(new WxChannel(), payDate, totalValue, paymentValue, couponValue, 0.0d);
}
//支付宝支付
public static PayResult createAliPayResult(PayChannel payChannel, Date payDate, Double totalValue, Double paymentValue, Double couponValue, Double loanValue) {
return new PayResult(new AliPayChannel(), payDate, totalValue, paymentValue, couponValue, 0.0d);
}
//注意这个时候我们的全包构造函数 变成了private
private PayResult(PayChannel payChannel, Date payDate, Double totalValue, Double paymentValue, Double couponValue, Double loanValue) {
this.payChannel = payChannel;
this.payDate = payDate;
this.totalValue = totalValue;
this.paymentValue = paymentValue;
this.couponValue = couponValue;
this.loanValue = loanValue;
}
增加了几个静态的create 函数,然后将我们的全包构造函数进行收敛 设置为private. 极大的限制了调用者的权限, 从而在一定程度上可以避免程序员的犯错。 而且这种改动也不会伤筋动骨,对于老的代码调用处来说,只要更换一下函数名即可:
public class TestMain {
public static void main(String[] args) {
PayResult p1 = PayResult.createUnionPayResult(new Date(), 3.1d, 3.1d);
PayResult p2 = PayResult.createAliPayResult(new AliPayChannel(), new Date(), 5.2d, 3.2d, 3.1d, 0.1d);
PayResult p3 = PayResult.createWxPayResult(new Date(), 3.1d, 2.1d, 1.0d);
}
}
这样看起来,不但可读性大大提升,安全性也较好,再也不用担心有其他同事用错了。