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