如何用最小代价,迅速提高你项目中“shit”代码的可读性(妈妈再也不用担心我删库跑路啦)

it2024-12-01  16

前言

这一章,我们主要学习如何增强代码的可读性。有人说过 最好的模式恰恰是是那些他认为会被别人嘲笑的模式 在我自己学习设计模式的过程中,我常常会觉得学起来很快,但是实际运用起来却根本想不起来要用哪个,即使遇到了 也不敢改,怕改出线上故障。最终写着写着就又回到了针对业务编程。

作者:vivo祁同伟 链接:https://juejin.im/post/6844904047397322759

正文

举个最简单的例子,阿里的java编码规约大家肯定都看过,甚至多数人可能都装了阿里的java代码检测插件,其中有一条就是当你的函数行数超过一定范围的时候就会提示warning告诉你函数太长,需要优化了。很多人不明白为什么,甚至阿里官方给出的理由是 多数人电脑的一屏 只能看到n行的代码,所以这里要增加这个函数行数的校验。

实际上这么多年我自己工作下来的感觉是上述的理由比较牵强,难道一个短函数就不需要优化了么?可以看看看下面的函数,这个函数也很短,但是可读性却并不好。

public class CustomList { private boolean readOnly; //默认的数组实际存储的元素个数 为0, private int size; //默认数组的长度为5 private Object[] elements = new Object[5]; /** * 只读模式下 才可以添加元素, * 如果数组长度足够 那么就直接往后面添加一个元素 * 如果数组长度不够 则将数组的长度增加10 * 然后再进行赋值 * * @param element */ public void add(Object element) { if (!readOnly) { int newSize = size + 1; if (newSize > elements.length) { Object[] newElements = new Object[elements.length + 10]; for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; } elements[size++] = element; } } }

优化的第一步:

public void add(Object element) { //只读模式就啥也不做 if (readOnly) { return; } if (atCapacity()) { Object[] newElements = new Object[elements.length + 10]; for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; } elements[size++] = element; } //是否需要扩容 private boolean atCapacity() { int newSize = size + 1; return newSize > elements.length; }

第二步:

public void add(Object element) { //只读模式就啥也不做 if (readOnly) { return; } if (atCapacity()) { grow(); } addElement(element); } //是否需要扩容 private boolean atCapacity() { int newSize = size + 1; return newSize > elements.length; } //扩容 private void grow() { Object[] newElements = new Object[elements.length + 10]; for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; } //增加一个元素 private void addElement(Object element) { elements[size++] = element; }

现在我们的add方法 仅仅就只有5行代码了。而可读性和第一版相比,真是天差地别。

如果你的类里面,public方法特别多,但是private方法特别少,那我觉得就一定还有优化的空间。

如果希望保持系统的简单性,那么就要尽可能的应用上述的组合方法进行细节上的重构,将一个个复杂的public方法,重构成一个个简单的private方法,最终对外暴露的public方法,只存在业务流程上对private方法的调用。

下面继续看另外一个问题,在很多老项目中,屎山代码最大的特点就是if else太多,很多人即使知道有个叫策略模式的方法可以解决这个问题,但是却不敢下手,下面介绍一个例子,体会一下如何针对屎山代码的if else 动手。

采用这篇文章里的PayResult类, 现在往里面增加一个方法, 获取这次支付结果的积分。毕竟现在电商里买东西总要返点积分给你的。

//获取积分 public double getIntegral() { //银联支付 按照1.5实际支付额度 返回积分 if (payChannel instanceof BankChannel) { return paymentValue * 1.5; } else if (payChannel instanceof WxChannel) { //微信支付就按照实际支付额度两倍,然后减去券的金额 return paymentValue * 2 - couponValue; } else if (payChannel instanceof AliPayChannel) { //支付宝支付*2 ,然后还可以加上花呗支付的积分 马爸爸牛逼 return paymentValue * 2 + loanValue * 1; } return 0; }

这样的代码项目里肯定不少见,我们现在来看看,怎么在一个成熟的系统里面,有惊无险的将这段代码优化一下。扩大一下可读性和可维护性。 毕竟很多人都知道大概怎么减少if else,但是真正实操起来 就往往做不到,不知道怎么做,也不敢做。

我们先弄一个积分策略类:

//积分策略 public class IntegralStrategy { public double getIntegral() { return 0; } }

然后把我们实际的积分算法逻辑 放到这个策略类里面

显然我们还需要一些参数,否则这些逻辑中需要引用的变量是找不到的 将外部的引用传进去:

//积分策略 public class IntegralStrategy { public double getIntegral(PayResult payResult) { //银联支付 按照1.5实际支付额度 返回积分 if (payResult.getPayChannel() instanceof BankChannel) { return payResult.getPaymentValue() * 1.5; } else if (payResult.getPayChannel() instanceof WxChannel) { //微信支付就按照实际支付额度两倍,然后减去券的金额 return payResult.getPaymentValue() * 2 - payResult.getCouponValue(); } else if (payResult.getPayChannel() instanceof AliPayChannel) { //支付宝支付*2 ,然后还可以加上花呗支付的积分 马爸爸牛逼 return payResult.getPaymentValue() * 2 + payResult.getLoanValue() * 1; } return 0; } }

然后修改一下我们的PayResult主类:

//获取积分 public double getIntegral() { return integralStrategy.getIntegral(this); } //注意这个时候我们的全包构造函数 变成了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; //也仅仅在这个构造函数 这里增加了一行代码 integralStrategy=new IntegralStrategy(); }

到这里,我们完成了初步的一个解耦工作,但是整体if else 的逻辑 还没有完全去除,只是挪了一个地方而已, 继续优化.

这里需要注意的是:如果你的if else逻辑里面不需要主类太多的参数,那么也没必要直接传递主类的引用,只要 直接传递参数就可以,这里为了演示方便,我们直接传递了主类作为参数。

只传递参数,而不传递主类的引用有一个好处:只涉及上下文类与这些策略类的最小耦合。

先去除if else

//积分策略 public abstract class IntegralStrategy { abstract double getIntegral(PayResult payResult); } public class AliPayIntegralStrategy extends IntegralStrategy { @Override double getIntegral(PayResult payResult) { return payResult.getPaymentValue() * 2 + payResult.getLoanValue() * 1; } } public class UnionPayIntegralStrategy extends IntegralStrategy { @Override double getIntegral(PayResult payResult) { return payResult.getPaymentValue() * 1.5; } } public class WxPayIntegralStrategy extends IntegralStrategy { @Override double getIntegral(PayResult payResult) { return payResult.getPaymentValue() * 2 - payResult.getCouponValue(); } }

然后 更改一下我们的PayResult的构造方法:

//银联支付 注意看最后一个参数 public static PayResult createUnionPayResult(Date payDate, Double totalValue, Double paymentValue) { return new PayResult(new BankChannel(), payDate, totalValue, paymentValue, 0.0d, 0.0d,new UnionPayIntegralStrategy()); } //微信支付 注意看最后一个参数 public static PayResult createWxPayResult(Date payDate, Double totalValue, Double paymentValue, Double couponValue) { return new PayResult(new WxChannel(), payDate, totalValue, paymentValue, couponValue, 0.0d,new WxPayIntegralStrategy()); } //支付宝支付 注意看最后一个参数 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,new AliPayIntegralStrategy()); } //注意看最后一个参数 private PayResult(PayChannel payChannel, Date payDate, Double totalValue, Double paymentValue, Double couponValue, Double loanValue, IntegralStrategy integralStrategy) { this.payChannel = payChannel; this.payDate = payDate; this.totalValue = totalValue; this.paymentValue = paymentValue; this.couponValue = couponValue; this.loanValue = loanValue; this.integralStrategy = integralStrategy; }

到此,我们就将整个积分系统 完全重构完毕。

文末

对文章有何见解,或者有何技术问题,都可以在评论区一起留言讨论,一定会回复的。 也欢迎大家来我的B站找我玩,各类Android架构师进阶技术难点的视频讲解,任君白嫖~ B站直通车:https://space.bilibili.com/484587989

喜欢文章的小伙伴别忘了点个关注,留个赞再走呗~

最新回复(0)