如何用最小代價迅速提升你項目中「屎山」代碼的可讀性

這一章,咱們主要學習如何加強代碼的可讀性。有人說過 最好的模式偏偏是是那些他認爲會被別人嘲笑的模式 在我本身學習設計模式的過程當中,我經常會以爲學起來很快,可是實際運用起來卻根本想不起來要用哪一個,即便遇到了 也不敢改,怕改出線上故障。最終寫着寫着就又回到了針對業務編程。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;
    }
複製代碼

到此,咱們就將整個積分系統 徹底重構完畢。

相關文章
相關標籤/搜索