如何用最小代价迅速提升你项目中“屎山”代码的可读性

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

举个最简单的例子,阿里的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行代码了。而可读性和初版相比,真是天差地别。bash

若是你的类里面,public方法特别多,可是private方法特别少,那我以为就必定还有优化的空间。微信

若是但愿保持系统的简单性,那么就要尽量的应用上述的组合方法进行细节上的重构,将一个个复杂的public方法,重构成一个个简单的private方法,最终对外暴露的public方法,只存在业务流程上对private方法的调用ide

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

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

//获取积分
    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;
    }
复制代码

到此,咱们就将整个积分系统 彻底重构完毕。

相关文章
相关标签/搜索