設計模式之-策略模式2021-02-02

策略模式

策略模式的定義

策略模式(Strategy attern)是指,定義了算法家族陡蝇、分別封裝起來痊臭,讓他們之間可以互相替換,此模式讓算法的變化登夫,不會影響到使用算法的用戶广匙。

避免多重分支語句 if...else... 和 switch語句

應用場景 - 優(yōu)惠打折

1.加入系統(tǒng)中有很多類,而他們的區(qū)別僅僅在于他們的行為不同恼策。

2.一個系統(tǒng)需要動態(tài)地在集中算法中選擇一種鸦致。

策略模式是返回一個策略類,再由策略類決定做什么操作涣楷,而委派模式則是將一個任務全權委派出去分唾,只看結果,這也是策略模式和委派模式的區(qū)別之一

例如总棵,一個商品優(yōu)惠打折的例子

打折活動的規(guī)范接口

/**
 * 所有打折活動的規(guī)范接口
 */
public interface IPromotionStrategy {

    void doPromotion();
}

有以下幾種打折策略

/**
 * 優(yōu)惠打折策略-返現(xiàn)策略
 */
public class CashBackStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("付款成功之后鳍寂,返現(xiàn)到用戶賬號余額");
    }
}
/**
 *  優(yōu)惠打折策略-優(yōu)惠券抵扣
 */
public class CouponStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("領用優(yōu)惠券,直接抵扣商品費用");
    }
}
/**
 * 優(yōu)惠打折策略-拼團策略
 */
public class GroupByStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("拼團情龄,滿20人成團迄汛,全團享受8折優(yōu)惠");
    }
}
/**
 * 優(yōu)惠打折策略-不參與優(yōu)惠打折
 * 不參加優(yōu)惠
 */
public class EmptyPromotionStrategy  implements IPromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("不參加優(yōu)惠");
    }
}

接下來,我們需要一個活動類骤视,來對策略類進行管理

/**
 * 優(yōu)惠活動處理類
 */
public class PromotionActivity {
    IPromotionStrategy promotionStrategy;

    public PromotionActivity(IPromotionStrategy promotionStrategy) {
        this.promotionStrategy = promotionStrategy;
    }
    public void execute() {
        this.promotionStrategy.doPromotion();
    }
}

當用戶進行買單時鞍爱,選擇要使用的優(yōu)惠方式,因此专酗,會有一個用戶選擇優(yōu)惠方式睹逃,得到打折策略的過程,此過程就需要使用到策略

因為每個打折策略,不會因為用戶或商品變化而變化沉填,因此這里可以結合單例疗隶,和工廠模式,根據(jù)用戶選擇的打折優(yōu)惠策略翼闹,來生產一個打折優(yōu)惠策略類

首先斑鼻,我們創(chuàng)建一個選擇策略的工廠類

工廠類-1私有構造方法

私有構造方法,并給出一個全局獲取對象的方法接口

public class PromotionStrategyFactory {
    private PromotionStrategyFactory() {};
    
    /** 工廠 */
    public static IPromotionStrategy getPromotionStrategy(String promotionKey) {
        //TODO 待實現(xiàn)
    }
}
構建打折優(yōu)惠策略KEY

將現(xiàn)有的打折優(yōu)惠策略KEY構建成一個枚舉或是常亮猎荠,這里用interface構建成常量

private interface PromotionKey {
    String COUPON = "COUPON";
    String CASHBACK = "BASHBACK";
    String GROUP_BY = "GROUP_BY";
}
注冊式單例

在工廠類中添加靜態(tài)變量 PROMOTION_STRATEGY_MAP并用注冊式單例的方式坚弱,將現(xiàn)有的打折優(yōu)惠策略注冊到 工廠變量

private static Map<String, IPromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();

static { //注冊式單例 結合策略的KEY,對應到每個kEY的策略實現(xiàn)
    PROMOTION_STRATEGY_MAP.put(PromotionKey.COUPON, new CouponStrategy());
    PROMOTION_STRATEGY_MAP.put(PromotionKey.CASHBACK, new CashBackStrategy());
    PROMOTION_STRATEGY_MAP.put(PromotionKey.GROUP_BY, new GroupByStrategy());
}
策略實現(xiàn)

在工廠提供的全局獲取對象方法中實現(xiàn)策略

    /** 用戶未選擇打折優(yōu)惠策略時关摇,不參與優(yōu)惠策略的默認變量 */
    private static final IPromotionStrategy NON_PROMOTION = new EmptyPromotionStrategy();
    /** 工廠 */
    public static IPromotionStrategy getPromotionStrategy(String promotionKey) {
        //策略
        IPromotionStrategy iPromotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
        return iPromotionStrategy == null ? NON_PROMOTION : iPromotionStrategy;
    }

由于PROMOTION_STRATEGY_MAP中將系統(tǒng)已有的打折優(yōu)惠策略都注冊進去了荒叶,此處,根據(jù)用戶傳入的策略KEy输虱,獲取到一個策略些楣,并返回

策略工廠完整代碼

/**
 * 優(yōu)惠策略的工廠
 */
public class PromotionStrategyFactory {
    private PromotionStrategyFactory() {};

    private static Map<String, IPromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();

    static { //注冊式單例
        PROMOTION_STRATEGY_MAP.put(PromotionKey.COUPON, new CouponStrategy());
        PROMOTION_STRATEGY_MAP.put(PromotionKey.CASHBACK, new CashBackStrategy());
        PROMOTION_STRATEGY_MAP.put(PromotionKey.GROUP_BY, new GroupByStrategy());
    }

    private static final IPromotionStrategy NON_PROMOTION = new EmptyPromotionStrategy();

    /** 工廠 */
    public static IPromotionStrategy getPromotionStrategy(String promotionKey) {
        //策略
        IPromotionStrategy iPromotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
        return iPromotionStrategy == null ? NON_PROMOTION : iPromotionStrategy;
    }


    private interface PromotionKey {
        String COUPON = "COUPON";
        String CASHBACK = "BASHBACK";
        String GROUP_BY = "GROUP_BY";
    }
}
測試-優(yōu)惠打折
    public static void main(String[] args) {
        //優(yōu)化之后
        String promotionKey = PromotionStrategyFactory.PromotionKey.GROUP_BY;
        /** 根據(jù)優(yōu)惠類型,調用優(yōu)惠類型工廠以及中的策略 */
        PromotionActivity activity = new PromotionActivity(PromotionStrategyFactory
                                                           .getPromotionStrategy(promotionKey));
        activity.execute();
    }

首先悼瓮,使用策略工廠戈毒,按照用戶選擇的優(yōu)惠打折策略,獲取到具體的優(yōu)惠打折策略横堡,然后在執(zhí)行優(yōu)惠打折方法execute();

應用場景-支付

通常埋市,我們在用餐時,會掃描商家給出的二維碼命贴,商家的二維碼是固定的道宅,我們既可以用支付寶、微信胸蛛、京東金融污茵、以及云閃付支付(假設上述商家都開通了支付)那么當我們使用對應的APP掃碼之后,發(fā)起支付時葬项,支付的系統(tǒng)服務方會根據(jù)用戶掃碼客戶單的APP返回的對應TYPE決定是那種支付泞当,然后在決定使用哪種支付的實現(xiàn),來完成本次支付民珍,這就是一個典型的策略實現(xiàn)邏輯襟士。進一步用策略類來模擬一下這個過程

先來寫幾個服務業(yè)務的類,

統(tǒng)一的返回消息對象

public class MessageResult implements Serializable {

    private int code;
    private Object data;
    private String msg;

    public MessageResult(String msg, int code, Object data) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }
    @Override
    public String toString() {
        return "支付狀態(tài){" +
                "code=" + code +
                ", data=" + data +
                ", msg='" + msg + '\'' +
                '}';
    }
}

訂單類

/**
 * 支付策略場景-訂單類
 */
public class Order {
    private String uid;
    private String goodsName;
    private String orderId;
    private double amount;

    public Order(String uid, String goodsName, String orderId, double amount) {
        this.uid = uid;
        this.goodsName = goodsName;
        this.orderId = orderId;
        this.amount = amount;
    }

    public MessageResult pay(String peyTypeCode) {
        //根據(jù)支付code 獲取到具體的支付策略
        IPayment iPayment = PayStrategy.get(peyTypeCode);
        System.out.printf("歡迎使用" + iPayment.getName() + "支付");
        System.out.println("本次支付金額 :" + this.amount + "元");
        return iPayment.pay(this.uid, this.amount);
    }
}

其中IPayment是所有支付策略實現(xiàn)的規(guī)范抽象嚷量,包含了支付方式名稱陋桂,查詢用戶對應的余額,以及發(fā)起支付的抽象蝶溶。

/**
 * 支付抽象
 */
public abstract class IPayment {
    //獲取支付名稱
    public abstract String getName();
    //查詢余額
    protected abstract double queryBalance(String uid);
    //調用支付
    public MessageResult pay(String uid, double amount) {
        if (queryBalance(uid) < amount) {
            return new MessageResult("支付失敗", 502, "余額不足");
        }
        return new MessageResult("支付成功", 200, "支付金額:".concat(String.valueOf(amount)));
    }
}

下面我們模式三個支付方的策略

/**
 * 微信支付
 */
public class WechatPay extends IPayment {
    @Override
    public String getName() {
        return "微信支付";
    }

    @Override
    protected double queryBalance(String uid) {
        return 3;
    }
}
/**
 * 支付寶支付的實現(xiàn)
 */
public class Alipay extends IPayment {
    @Override
    public String getName() {
        return "支付寶";
    }

    @Override
    protected double queryBalance(String uid) {
        return 500;
    }
}
/**
 * 京東支付的實現(xiàn)
 */
public class JDPay extends IPayment {
    @Override
    public String getName() {
        return "京東白條";
    }

    @Override
    protected double queryBalance(String uid) {
        return 350;
    }
}

結合上面的優(yōu)惠打折案例嗜历,這里也類似,構建集中支付方式的枚舉|常量,在構建一個工廠方法梨州,結合策略痕囱,實現(xiàn)感覺支付枚舉,得到具體的支付策略的方法暴匠。

/**
 * 支付策略
 */
public class PayStrategy {
    
    //系統(tǒng)支付的支付方式常量
    public static final String ALI_PAY = "Alipay";
    public static final String JD_PAY = "JDPay";
    public static final String WECHAT_PAY = "WechatPay";
    public static final String DEFAULT_PAY = ALI_PAY;

    //注冊式單例 保存所有策略的對象
    private static Map<String, IPayment> payStrategy = new HashMap<>();

    static {
        //根據(jù)支付方式常量咐蝇,分配對應的支付實現(xiàn)策略
        payStrategy.put(ALI_PAY, new Alipay());
        payStrategy.put(JD_PAY, new JDPay());
        payStrategy.put(WECHAT_PAY, new WechatPay());
    }

    /**
     * 根據(jù)統(tǒng)一入口,實現(xiàn)動態(tài)策略
     * @param payTypeCode
     * @return
     */
    public static IPayment get(String payTypeCode) {
        if (payStrategy.containsKey(payTypeCode)) {
            return payStrategy.get(payTypeCode);
        }
        return payStrategy.get(DEFAULT_PAY); //用戶沒傳參巷查,或傳入有誤時,使用默認的DEFAULT_PAY策略進行支付
    }
測試-多種支付之間的選擇
public static void main(String[] args) {
    //實例化一個訂單
    Order order = new Order("1","糖果", "20210201001", 350);
    String payType = PayStrategy.ALI_PAY;
    //
    MessageResult alipay = order.pay(payType);
    System.out.println(alipay);
}

可以清楚的看到抹腿,當后續(xù)再需要添加一個支付方式時岛请,我們只需要添加具體支付的業(yè)務策略,然后再將該策略添加到工廠類中即可警绩,原本業(yè)務基本上可以不用改動崇败。是不是很棒?肩祥!~后室!@

strategy

設計模式,經驗的總結岸霹,知識為了解決實際問題,簡化我們的工作量将饺, 以及工作效率贡避。

框架和JDk中的策略模式例子

Comparator 比較器

Comparable

InstantiationStrategy類初始化策略

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

優(yōu)點

符合開閉原則

避免使用多重條件轉移語句 ,如 if...else...語句予弧、switch語句

使用策略模式可以提高算法的保密性和安全性

缺點

客戶端必須知道所有的策略刮吧,并且自行決定使用哪一個策略類。

代碼中會產生非常多策略類掖蛤,增加維護難度杀捻。

JDK源碼

TreeMap

public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

Spring中的源碼

InstantiationStrategy //類初始化策略
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蚓庭,隨后出現(xiàn)的幾起案子致讥,更是在濱河造成了極大的恐慌,老刑警劉巖彪置,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拄踪,死亡現(xiàn)場離奇詭異,居然都是意外死亡拳魁,警方通過查閱死者的電腦和手機惶桐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人姚糊,你說我怎么就攤上這事贿衍。” “怎么了救恨?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵贸辈,是天一觀的道長。 經常有香客問我肠槽,道長擎淤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任秸仙,我火速辦了婚禮嘴拢,結果婚禮上,老公的妹妹穿的比我還像新娘寂纪。我一直安慰自己席吴,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布捞蛋。 她就那樣靜靜地躺著孝冒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拟杉。 梳的紋絲不亂的頭發(fā)上庄涡,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音搬设,去河邊找鬼啼染。 笑死,一個胖子當著我的面吹牛焕梅,可吹牛的內容都是我干的迹鹅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼贞言,長吁一口氣:“原來是場噩夢啊……” “哼斜棚!你這毒婦竟也來了?” 一聲冷哼從身側響起该窗,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤弟蚀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后酗失,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體义钉,經...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年规肴,在試婚紗的時候發(fā)現(xiàn)自己被綠了捶闸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夜畴。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖删壮,靈堂內的尸體忽然破棺而出贪绘,到底是詐尸還是另有隱情,我是刑警寧澤央碟,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布税灌,位于F島的核電站,受9級特大地震影響亿虽,放射性物質發(fā)生泄漏菱涤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一洛勉、第九天 我趴在偏房一處隱蔽的房頂上張望狸窘。 院中可真熱鬧,春花似錦坯认、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至劳吠,卻和暖如春引润,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痒玩。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工淳附, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蠢古。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓奴曙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親草讶。 傳聞我的和親對象是個殘疾皇子洽糟,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容

  • 策略模式(Strategy Pattern)也叫做政策模式(Policy Pattern),是一種行為型模式堕战。 一...
    l只為終點閱讀 582評論 1 3
  • 排序問題: 當要比較的是整數(shù)坤溃,浮點數(shù),都需要不同的排序方法嘱丢,并且當改變了要比較的東西時要重新排序薪介,這樣不好, 怎樣...
    tracy_668閱讀 313評論 0 2
  • 本文的主要內容: 介紹策略模式 示例商場購物打折策略的實現(xiàn) 策略模式總結 源碼分析策略模式的典型應用Java Co...
    小旋鋒的簡書閱讀 1,349評論 0 1
  • 設計原則 1.開閉原則2.依賴倒置原則3.單一職責原則4.接口隔離原則5.迪米特法則(最少知道原則)6.里式替換原...
    縱橫Top閱讀 567評論 0 0
  • 工廠模式類似于現(xiàn)實生活中的工廠可以產生大量相似的商品越驻,去做同樣的事情汁政,實現(xiàn)同樣的效果;這時候需要使用工廠模式道偷。簡單...
    舟漁行舟閱讀 7,718評論 2 17