如何在代碼中應用設計模式

爲何要使用設計模式

由於咱們的項目的需求是永遠在變的,爲了應對這種變化,使得咱們的代碼可以輕易的實現解耦和拓展。若是可以保證代碼一次寫好之後都不會再改變了,那能夠想怎麼寫怎麼寫了。算法

如何判斷那裏須要使用設計模式

image

在咱們實現中,有一些代碼是一次寫好後續基本不會改變的,或者不太須要擴展的,好比一些工具類等。有一部分是會常常變得,設計模式大多都應用在需求會變化的這一部分。分析這些代碼會如何變,選擇合適的設計模式來優化這部分代碼。設計模式

以促銷活動需求爲例

需求

爲了促進商品的銷售,各大電商品臺會在平時或者一些節日的時候退出一些促銷活動刺激用戶消費,活動的類型可能會各不相同,以下:app

  • 滿減,滿400減20
  • 代金卷,瑪莎拉蒂5元代金卷
  • 折扣,9折,8折
  • 每滿減,每滿200減10
  • 等等

其中有些能夠疊加,有些只能單獨使用。工具

簡單實現

上面的需求看起來仍是比較簡單的,可是若是考慮到咱們是不可能一次定義好全部的促銷活動類型,後續咱們可能會隨時都添加新的類型,要保證可以簡單的實現功能擴展,那就比較麻煩了。優化

先拿到需求的時候,也不用去想那麼多,挽起袖子就是一通操做:設計

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            switch (promotion){
                case 1:
                    //計算該類型折扣後的價格
                    break;
                case 2:
                    //計算該類型折扣後的價格
                    break;
                case 3:
                    //計算該類型折扣後的價格
                    break;
                //....
            }
        }
        return order.getResultPrice();
    }
}

單從功能實現上來講,上面的代碼已經完成了基本功能了。可是上面的代碼也是致命的,雖然看起來很簡單,可是那隻不過是由於大多數功能都用註釋代替了,換成實際代碼的話一個方法可能就得上千行。code

尤爲是當咱們須要添加新的促銷活動的話就須要在switch中添加新的類型,這對於開發來講簡直是災難,而且維護這些代碼也是一個麻煩。blog

優化一:單一職責原則

上面的代碼中,promotion(...)方法直接完成了全部的工做,可是咋咱們實際實現中最好讓一個方法的職責單一,只完成某一個功能,因此這裏咱們將對摺扣類型的判斷和計算價格分開:接口

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            switch (promotion){
                case 1:
                    calculate1(order);
                    break;
                case 2:
                    calculate2(order);
                    break;
                case 3:
                    calculate3(order);
                    break;
                //more promotion
            }
        }
        return order.getResultPrice();
    }
    
    public void calculate1(Order order){
        //計算使用折扣一後的價格
    }

    public void calculate2(Order order){
        //計算使用折扣二後的價格
    }

    public void calculate3(Order order){
        //計算使用折扣三後的價格
    }
    
    //more calculate
    
}

這裏咱們將折扣類型的判斷和計算價格分開,使得promotion(...)方法的代碼量大大下降,提高了代碼的可讀性。ci

優化二:策略模式

上面優化後的代碼提高了原有代碼的可讀性,可是原來OrderPromotion類代碼大爆炸的問題仍是沒有解決。針對這個問題,咱們但願可以將計算的代碼和當前代碼分離開,首先咱們能想到的就是定義一個類,而後將計算的代碼複製到這個類中,須要的時候就調用。這樣到的確是分離開了,可是徹底是治標不治本。在添加新的促銷活動是兩個類都要改。

因此咱們但願可以將不一樣的促銷活動的實現分離開,這樣對每一種活動的實現都是分開的,修改也不會影響其餘的,基於此咱們徹底能夠選擇策略模式來實現。

策略模式

策略模式的思想是針對一組算法,將每一種算法都封裝到具備共同接口的獨立的類中,從而是它們能夠相互替換。策略模式的最大特色是使得算法能夠在不影響客戶端的狀況下發生變化,從而改變不一樣的功能。

promotion

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            switch (promotion){
                case 1:
                    new PromotionType1Calculate(order);
                    break;
                case 2:
                    new PromotionType1Calculate(order);
                    break;
                case 3:
                    new PromotionType1Calculate(order);
                    break;
                //more promotion
            }
        }
        return order.getResultPrice();
    }
}

上面的代碼很明顯已經精簡不少了,到了如今若是須要添加一個促銷活動的話只需定義一個促銷類,實現PromotionCalculation接口而後在switch中添加便可。

優化三:工廠模式

上面的代碼雖然已經將促銷活動的實現分離開了,可是OrderPromotion仍是一直在變得,每一次添加或者下線活動都須要修改該類。如今咱們但願OrderPromotion是不變的,將PromotionCalculation的實例化剝離開來。建立類很明顯是使用工廠設計模式了。

OrderPromotion

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            PromotionFactory.getPromotionCalculate(promotion).calculate(order);
        }
        return order.getResultPrice();
    }
}

類的建立工做交給工廠來實現。

PromotionFactory

public class PromotionFactory {
    
    public static PromotionCalculate getPromotionCalculate(int promotion){
        switch (promotion){
            case 1:
                return new PromotionType1Calculate(order);
            break;
            case 2:
                return new PromotionType1Calculate(order);
            break;
            case 3:
                return new PromotionType1Calculate(order);
            break;
            //more promotion
        }
        return null;
    }
}

使用工廠模式後OrderPromotion類就不須要改了,每一次添加新的促銷活動後只須要在工廠類中添加便可。

優化四:配置+反射

上面的代碼還存在的問題在於每一次須要添加新的促銷活動的時候仍是須要修改工廠類中的代碼,這裏咱們經過配置文件加反射的方式來解決。

定義映射配置文件

mapping.properties

1=design.order.PromotionType1Calculate
2=design.order.PromotionType2Calculate
3=design.order.PromotionType3Calculate

PromotionFactory

public class PromotionFactory {
    
    private static Map<Integer, String> mapping = new HashMap<Integer, String>();

    static {
        try {
            Properties pps = new Properties();
            pps.load(new FileInputStream("Test.properties"));
            Iterator<String> iterator = pps.stringPropertyNames().iterator();
            while(iterator.hasNext()){
                String key=iterator.next();
                mapping.put(Integer.valueOf(key), pps.getProperty(key));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static PromotionCalculate getPromotionCalculate(int promotion) throws Exception {
        if(mapping.containsKey(promotion)){
            String beanName = mapping.get(promotion);
            return Class.forName(beanName).newInstance();
        }
        return null;
    }
}

經過上面的代碼就能夠實現不改變已有代碼的前提下實現對功能的靈活擴展。固然,這裏的代碼只是做爲演示用的,實際上能夠改進的地方還有很多,像最後反射效率較低,也能夠經過其餘的方式來實現。

小結

設計模式是咱們必定要了解的東西,熟悉設計模式能讓咱們設計出易於擴展和維護的代碼結構。可是並非任何地方都須要上設計模式,應該結合咱們的項目實際進行分析是否須要設計模式,使用哪一種設計模式。

相關文章
相關標籤/搜索