成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

漫話:如何給女朋友解釋什么是策略模式?

fancyLuo / 2783人閱讀

摘要:策略策略,指的是可以實現(xiàn)目標(biāo)的方案集合,在某些特定情況下,策略之間是可以相互替換的。如何計算金額我們先拿點外賣中會員折扣活動舉例子來說明一下吧。這就是策略模式。策略模式提供了管理相關(guān)的算法族的辦法。

?

周末無事,窩在家里面看《權(quán)力的游戲第八季》,看的很是津津有味,雖然感覺有一點點要爛尾,但是我還是忍不住要去看到底誰可以坐上鐵王座。

女朋友在一旁點外賣,好像是在使用優(yōu)惠的時候遇到了一點點小問題。

策略

策略,指的是可以實現(xiàn)目標(biāo)的方案集合,在某些特定情況下,策略之間是可以相互替換的。

比如我們在外賣平臺上看到的這些優(yōu)惠。滿減、會員和紅包等,每一個大項優(yōu)惠都具體包含了多個優(yōu)惠方案。如滿減活動中,可以同時有滿20減15、滿50減30等。會員包含普通會員、超級會員等。

每一個優(yōu)惠方式下面的多個優(yōu)惠方案,其實都是一個策略。這些策略之間是相互排斥、可替換的。并且是有一定的優(yōu)先級順序的。

如上圖,一筆訂單中共使用到了4種優(yōu)惠,可以說我們組合使用了四種優(yōu)惠策略。

如何計算金額

我們先拿點外賣中會員折扣活動舉例子來說明一下吧。外賣平臺上的某家店鋪為了促銷,設(shè)置了多種會員優(yōu)惠,其中包含超級會員折扣8折、普通會員折扣9折和普通用戶沒有折扣三種。

我們希望用戶在付款的時候,根據(jù)用戶的會員等級,就可以知道用戶符合哪種折扣策略,進而進行打折,計算出應(yīng)付金額。

代碼中可以這樣寫:

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {

    if (BuyerType.SUPER_VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.8));
    }

    if (BuyerType.VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.9));
    }

    return orderPrice;
}

以上代碼比較簡單,就是在代碼中通過if-else進行邏輯判斷,不同類型的會員享受不同的折扣價。

再增加一種會員類型

這個時候,平臺增加了一種店鋪專屬會員,這種會員可以專享某一個店鋪菜品的7折優(yōu)惠,那么代碼又要改成以下這樣:

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {

    if (BuyerType.PARTICULARLY_VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.7));
    }

    if (BuyerType.SUPER_VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.8));
    }

    if (BuyerType.VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.9));
    }

    return orderPrice;
}

會員折扣變化

后面,隨著業(yè)務(wù)發(fā)展,新的需求要求專屬會員要在店鋪下單金額大于30元的時候才可以享受優(yōu)惠。代碼就需要再次修改:

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {

    if (BuyerType.PARTICULARLY_VIP.name().equals(buyerType)) {
        if (orderPrice.compareTo(new BigDecimal(30)) > 0) {
            return orderPrice.multiply(new BigDecimal(0.7));
        }
    }

    if (BuyerType.SUPER_VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.8));
    }

    if (BuyerType.VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.9));
    }

    return orderPrice;
}

接著,又有一個{{BANNED}}的需求,如果用戶的超級會員已經(jīng)到期了,并且到期時間在一周內(nèi),那么就對用戶的單筆訂單按照超級會員進行折扣,并在收銀臺進行強提醒,引導(dǎo)用戶再次開通會員,而且折扣只進行一次。代碼需要做如下修改:

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {

    if (BuyerType.PARTICULARLY_VIP.name().equals(buyerType)) {
        if (orderPrice.compareTo(new BigDecimal(30)) > 0) {
            return orderPrice.multiply(new BigDecimal(0.7));
        }
    }

    if (BuyerType.SUPER_VIP.name().equals(buyerType)) {
        return orderPrice.multiply(new BigDecimal(0.8));
    }

    if (BuyerType.VIP.name().equals(buyerType)) {
        int  superVipExpiredDays  = getSuperVipExpiredDays();
        int superVipLeadDiscountTimes  = getSuperVipLeadDiscountTimes();
        if(superVipExpiredDays < 7 && superVipLeadDiscountTimes =0){
            updateSuperVipLeadDiscountTimes();
            return orderPrice.multiply(new BigDecimal(0.8));
        }
        return orderPrice.multiply(new BigDecimal(0.9));
    }

    return orderPrice;
}


為什么要使用策略模式

以上代碼,所有關(guān)于會員折扣的代碼全部都寫在了一個calPrice方法中,增加或者減少一種會員類型,就需要改動到整個方法。還要考慮這種會員折扣的優(yōu)先級問題。

除了增加會員類型外,其中任何一種會員類型的折扣策略發(fā)生變化,也需要改動到整個算法。

這就會導(dǎo)致這個算法越來越臃腫,進而得到的后果就是代碼行數(shù)越來越多,開發(fā)人員改動一點點代碼都需要把所有功能全部都回歸一遍。

就比如說我只是把超級會員的折扣從8折改為8.5折,這時候因為代碼都在一起,那就需要在上線的時候?qū)τ跁T折扣的所有功能進行回歸。

?

久而久之,這段代碼就變成了一段誰都不愿改,誰都不敢改的代碼。俗稱"屎山"。這種代碼會使代碼有極低的可讀性、可維護性、可擴展性,并且回歸成本較高。

策略模式

我們說日常生活中,我們要實現(xiàn)目標(biāo),有很多方案,每一個方案都被稱之為一個策略。在軟件開發(fā)中也常常遇到類似的情況,實現(xiàn)某一個功能有多個途徑,此時可以使用一種設(shè)計模式來使得系統(tǒng)可以靈活地選擇解決途徑,也能夠方便地增加新的解決途徑。這就是策略模式。

策略模式(Strategy Pattern),指的是定義一系列算法,將每一個算法封裝起來,并讓它們可以相互替換。策略模式讓算法獨立于使用它的客戶而變化。

特別說明一下,策略模式只適用管理一組同類型的算法,并且這些算法是完全互斥的情況。也就是說任何時候,多個策略中只有一個可以生效的那一種。如滿減中的滿20減10與滿30減20之間;普通會員折扣與超級會員折扣之間等。

在策略模式中,定義一些獨立的類來封裝不同的算法,每一個類封裝一個具體的算法,在這里,每一個封裝算法的類我們都可以稱之為策略(Strategy),為了保證這些策略的一致性,一般會用一個抽象的策略類來做算法的定義,而具體每種算法則對應(yīng)于一個具體策略類。

要實現(xiàn)策略模式,肯定離不開策略。如前面提到的超級會員、普通會員、專屬會員等的折扣其實都是策略。完全可以通過策略模式來實現(xiàn)。

實現(xiàn)策略模式主要包含的角色如下:

抽象策略類

先定義一個接口,這個接口就是抽象策略類,該接口定義了計算價格方法,具體實現(xiàn)方式由具體的策略類來定義。

public interface Buyer {

    /**
     * 計算應(yīng)付價格
     */
    public BigDecimal calPrice(BigDecimal orderPrice);
}

具體策略類

針對不同的會員,定義三種具體的策略類,每個類中都分別實現(xiàn)計算價格方法。

/**
 * 專屬會員
 */
public class ParticularlyVipBuyer implements Buyer {

    @Override
    public BigDecimal calPrice(BigDecimal orderPrice) {
         if (orderPrice.compareTo(new BigDecimal(30)) > 0) {
            return orderPrice.multiply(new BigDecimal(0.7));
        }
    }
}


/**
 * 超級會員
 */
public class SuperVipBuyer implements Buyer {

    @Override
    public BigDecimal calPrice(BigDecimal orderPrice) {
        return orderPrice.multiply(new BigDecimal(0.8));
    }
}


/**
 * 普通會員
 */
public class VipBuyer implements Buyer {

    @Override
    public BigDecimal calPrice(BigDecimal orderPrice) {
        int  superVipExpiredDays  = getSuperVipExpiredDays();
        int superVipLeadDiscountTimes  = getSuperVipLeadDiscountTimes();
        if(superVipExpiredDays < 7 && superVipLeadDiscountTimes =0){

            return orderPrice.multiply(new BigDecimal(0.8));
        }
        return orderPrice.multiply(new BigDecimal(0.9));
    }
}

上面幾個類的定義體現(xiàn)了封裝變化的設(shè)計原則,不同會員的具體折扣方式改變不會影響到其他的會員。

定義好了抽象策略類和具體策略類之后,我們再來定義上下文類,所謂上下文類,就是集成算法的類。這個例子中就是收銀臺系統(tǒng)。采用組合的方式把會員集成進來。

public class Cashier {

    /**
     * 會員,策略對象
     */
    private Buyer buyer;

    public Cashier(Buyer buyer){
        buyer = buyer;
    }

    public BigDecimal quote(BigDecimal orderPrice) {
        return this.buyer.calPrice(orderPrice);
    }
}

這個Cashier類就是一個上下文類,該類的定義體現(xiàn)了多用組合,少用繼承、針對接口編程,不針對實現(xiàn)編程兩個設(shè)計原則。

由于這里采用了組合+接口的方式,后面我們在推出其他類型會員的時候無須修改Cashier類。只要再定義一個類實現(xiàn)Buyer接口 就可以了。

除了增加會員類型以外,我們想要修改某個會員的折扣情況的時候,只需要修改該會員對應(yīng)的策略類就可以了,不需要修改到其他的策略。也就控制了變更的范圍。大大降低了成本。

下面定義一個客戶端來測試一下:

public class Test {

    public static void main(String[] args) {

        //選擇并創(chuàng)建需要使用的策略對象
        Buyer strategy = new VipBuyer();
        //創(chuàng)建上下文
        Cashier cashier = new Cashier(strategy);
        //計算價格
        BigDecimal quote = cashier.quote(300);
        System.out.println("普通會員商品的最終價格為:" + quote.doubleValue());

        strategy = new SuperVipBuyer();
        cashier = new Cashier(strategy);
        quote = cashier.quote(300);
        System.out.println("超級會員商品的最終價格為:" + quote.doubleValue());
    }
}

輸出結(jié)果:

//普通會員商品的最終價格為:270.0
//超級會員商品的最終價格為:240.0

從上面的示例可以看出,策略模式僅僅封裝算法,提供新的算法插入到已有系統(tǒng)中,策略模式并不決定在何時使用何種算法。在什么情況下使用什么算法是由客戶端決定的。

策略模式的優(yōu)缺點

策略模式可以充分的體現(xiàn)面向?qū)ο笤O(shè)計原則中的封裝變化、多用組合,少用繼承、針對接口編程,不針對實現(xiàn)編程等原則。

策略模式具有以下特點:

策略模式的關(guān)注點不是如何實現(xiàn)算法,而是如何組織、調(diào)用這些算法,從而讓程序結(jié)構(gòu)更靈活,具有更好的維護性和擴展性。

策略模式中各個策略算法是平等的。對于一系列具體的策略算法,大家的地位是完全一樣的,正因為這個平等性,才能實現(xiàn)算法之間可以相互替換。所有的策略算法在實現(xiàn)上也是相互獨立的,相互之間是沒有依賴的。所以可以這樣描述這一系列策略算法:策略算法是相同行為的不同實現(xiàn)。

運行期間,策略模式在每一個時刻只能使用一個具體的策略實現(xiàn)對象,雖然可以動態(tài)地在不同的策略實現(xiàn)中切換,但是同時只能使用一個。

如果所有的具體策略類都有一些公有的行為。這時候,就應(yīng)當(dāng)把這些公有的行為放到共同的抽象策略角色Strategy類里面。當(dāng)然這時候抽象策略角色必須要用Java抽象類實現(xiàn),而不能使用接口。

但是,編程中沒有銀彈,策略模式也不例外,他也有一些缺點,我們先來回顧總結(jié)下他的優(yōu)點:

策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為,也可以靈活地增加新的算法或行為。

策略模式提供了管理相關(guān)的算法族的辦法。策略類的等級結(jié)構(gòu)定義了一個算法或行為族。恰當(dāng)使用繼承可以把公共的代碼移到父類里面,從而避免代碼重復(fù)。

使用策略模式可以避免使用多重條件(if-else)語句。多重條件語句不易維護,它把采取哪一種算法或采取哪一種行為的邏輯與算法或行為的邏輯混合在一起,統(tǒng)統(tǒng)列在一個多重條件語句里面,比使用繼承的辦法還要原始和落后。

但同時,他也有如下缺點:

客戶端必須知道所有的策略類,并自行決定使用哪一個策略類。這就意味著客戶端必須理解這些算法的區(qū)別,以便適時選擇恰當(dāng)?shù)乃惴悺_@種策略類的創(chuàng)建及選擇其實也可以通過工廠模式來輔助進行。

由于策略模式把每個具體的策略實現(xiàn)都多帶帶封裝成為類,如果備選的策略很多的話,那么對象的數(shù)目就會很可觀??梢酝ㄟ^使用享元模式在一定程度上減少對象的數(shù)量。


最后,附上本文內(nèi)容的思維導(dǎo)圖:


文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7389.html

相關(guān)文章

  • 漫話如何朋友解釋滅霸的指響并不真隨機"消滅"半數(shù)宇宙人口的?

    摘要:軟件實現(xiàn)的是偽隨機數(shù)。有限狀態(tài)機不能產(chǎn)生真正的隨機數(shù)的。復(fù)聯(lián)中,滅霸打了指響之后,復(fù)仇者聯(lián)盟中存活和死亡的名單其實并不是隨機的??梢?,滅霸的指響抹除過程并不是隨機的。綜上,滅霸的指響抹除過程不符合隨機性不可預(yù)測性以及不可復(fù)現(xiàn)性。showImg(https://user-gold-cdn.xitu.io/2019/5/7/16a91fc63239db4d);周末,陪女朋友去電影院看了《復(fù)仇者聯(lián)...

    WalkerXu 評論0 收藏0
  • 漫話密碼存儲

    摘要:通俗一點說就是彩虹表犧牲了一點計算速度,換來的好處是較少的空間存儲更多的密碼數(shù)據(jù)。關(guān)于鹽的使用有一點需要說明加鹽的目的是為了增加提前構(gòu)造字典和彩虹表的代價,并不是為了加密或增加密碼的計算復(fù)雜性。 背景 密碼是用來進行鑒權(quán)(身份認證)一種手段,說白了就是證明你是誰。一般鑒權(quán)都可以總結(jié)為下面3種形式: 你知道什么? (如密碼,密碼提示問題等) 你有什么? (如信用卡,token卡等) 你...

    Godtoy 評論0 收藏0
  • 追本溯源:substr與substring歷史漫話

    摘要:基因追本溯源在編程語言的歷史長河中,曾經(jīng)出現(xiàn)過很多編程語言。的歷史繼承年,網(wǎng)景公司招募了,目的是將編程語言嵌入到中。網(wǎng)景公司決定,他們想創(chuàng)建的腳本語言將補充,并且應(yīng)該有一個類似的語法,排除采用,,或等其他語言。 引子: 很多時候,當(dāng)我要字符串截取時,我會想到substr和substring的方法,但是具體要怎么傳參數(shù)時,我總是記不住。哪個應(yīng)該傳個字符串長度,哪個又應(yīng)該傳個開始和結(jié)尾的下...

    gecko23 評論0 收藏0
  • 追本溯源:substr與substring歷史漫話

    摘要:基因追本溯源在編程語言的歷史長河中,曾經(jīng)出現(xiàn)過很多編程語言。的歷史繼承年,網(wǎng)景公司招募了,目的是將編程語言嵌入到中。網(wǎng)景公司決定,他們想創(chuàng)建的腳本語言將補充,并且應(yīng)該有一個類似的語法,排除采用,,或等其他語言。 引子: 很多時候,當(dāng)我要字符串截取時,我會想到substr和substring的方法,但是具體要怎么傳參數(shù)時,我總是記不住。哪個應(yīng)該傳個字符串長度,哪個又應(yīng)該傳個開始和結(jié)尾的下...

    FuisonDesign 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<