策略模式
策略模式的定義
策略模式(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 //類初始化策略