使用場景
公司最近在做直播功能,底層原來有一套直播API,現在新增一套網宿直播API。
考慮以後的擴充套件性,需要將兩套API進行統一管理。現在以網上的支付方式演示我對策略模式的理解。
支付方式
我們知道網上有很多支付方式。支付寶、微信、銀行卡、花唄...
我們以三種支付方式進行演示。
策略模式的組成有三部分
環境類(Context):用一個ConcreteStrategy物件來配置。維護一個對Strategy物件的引用。
可定義一個介面來讓Strategy訪問它的資料,在上一個例子中相當於Staff。
抽象策略類(Strategy):定義所有支援的演算法的公共介面。 Context使用這個介面來呼叫某ConcreteStrategy定義的演算法,
在上一個例子中相當於GrantReward。
具體策略類(ConcreteStrategy):以Strategy介面實現某具體演算法,在上一個例子中相當於GrantSuger,GrantMoonCake,GrantNone。
支付方式的組成也有三部分
支付策略介面(PayStrategy):定義支付方式
具體支付方式(AliPayStrategy、WxPayStrategy、CardPayStrategy):具體的支付演算法
支付策略上下文(PayStrategyContent):管理所有支付方式的引用,並根據使用者選擇引用對應的支付方式。
程式碼實現
支付策略介面(PayStrategy)
1 /**
2 * 支付策略介面
3 * @author JinXing
4 * @date 2019/7/12 13:58
5 */
6 public interface PayStrategy {
7
8
9
10 /**
11 *
12 * 選擇支付方式
13 * 支付寶
14 * 微信
15 * 銀行卡
16 * @return RemoteResult
17 */
18 RemoteResult<String> toPayHtml();
19
20
21 }
具體支付方式(AliPayStrategy)
1 /**
2 * 阿里pay
3 * @author JinXing
4 * @date 2019/7/12 14:36
5 */
6 @Service
7 public class AliPayStrategy implements PayStrategy {
8
9 @Override
10 public RemoteResult<String> toPayHtml() {
11
12 System.out.println("現在採用的支付方式為:支付寶支付......");
13
14 return null;
15 }
16 }
具體支付方式(WxPayStrategy)
1 /**
2 * 微信支付
3 * @author JinXing
4 * @date 2019/7/12 14:36
5 */
6
7 @Service
8 public class WxPayStrategy implements PayStrategy {
9
10 @Override
11 public RemoteResult<String> toPayHtml() {
12
13 System.out.println("現在採用的支付方式為:微信支付......");
14
15 return null;
16 }
17 }
具體支付方式(CardPayStrategy)
1 /**
2 * 銀行卡支付
3 * @author JinXing
4 * @date 2019/7/12 14:36
5 */
6
7 @Service
8 public class CardPayStrategy implements PayStrategy {
9
10 @Override
11 public RemoteResult<String> toPayHtml() {
12
13 System.out.println("現在採用的支付方式為:銀行卡支付......");
14
15 return null;
16 }
17 }
支付策略上下文(PayStrategyContent)
/**
* 支付策略上下文
* @author JinXing
* @date 2019/7/12 14:39
*/
@Component
public class PayStrategyContent {
/** 策略例項集合 */
private ConcurrentHashMap<String, PayStrategy> strategyMap = new ConcurrentHashMap<>(20);
/**
* 注入策略例項
* 如果使用的是構造器注入,可能會有多個引數注入進來。
*
* 如果使用的是field反射注入
*
* 如果使用的是setter方法注入,那麼你將不能將屬性設定為final。
*
* @param strategyMap
* 注意注入型別要是Map基礎型別
*/
@Autowired
public PayStrategyContent(Map<String, PayStrategy> strategyMap) {
//清空集合資料
this.strategyMap.clear();
if (!CollectionUtils.isEmpty(strategyMap)) {
strategyMap.forEach((beanName, payStrategy) -> {
if (StringUtils.isEmpty(beanName) || payStrategy == null) {
return;
}
this.strategyMap.put(beanName.toLowerCase(), payStrategy);
});
}
}
/**
* 選擇支付方式
* 支付寶、微信、銀行卡
*
* @param paymentEnums
*
* @return RemoteResult
*/
RemoteResult<String> toPayHtml(PaymentEnums paymentEnums) {
if (CollectionUtils.isEmpty(strategyMap)) {
return new RemoteResult<String>().error("策略例項集合初始化失敗,請檢查是否正確注入!");
}
return this.strategyMap.get(paymentEnums.getBeanName()).toPayHtml();
}
}
支付方式列舉(PaymentEnums)
**
* 支付方式列舉物件
* code -> 支付方式別名
* beanName -> 例項的名稱
*
* @author JinXing
* @date 2019/7/12 14:40
*/
public enum PaymentEnums {
/** 支付方式 */
ALI_PAY("ali_pay", AliPayStrategy.class.getSimpleName()),
WX_PAY("WX_PAY", WxPayStrategy.class.getSimpleName()),
CARD_PAY("card_pay", CardPayStrategy.class.getSimpleName()),
;
/** 列舉定義+描述 */
private String code;
private String beanName;
PaymentEnums(String code, String beanName) {
this.code = code;
this.beanName = StringUtils.isNotEmpty(beanName)?beanName.toLowerCase():null;
}
/** 根據code獲取對應的列舉物件 */
public static PaymentEnums getEnum(String code) {
PaymentEnums[] values = PaymentEnums.values();
if (null != code && values.length > 0) {
for (PaymentEnums value : values) {
if (value.code.equals(code)) {
return value;
}
}
}
return null;
}
/** 該code在列舉列表code屬性是否存在 */
public static boolean containsCode(String code) {
PaymentEnums anEnum = getEnum(code);
return anEnum != null;
}
/** 判斷code與列舉中的code是否相同 */
public static boolean equals(String code, PaymentEnums calendarSourceEnum) {
return calendarSourceEnum.code.equals(code);
}
public String getCode() {
return code;
}
public String getBeanName() {
return beanName;
}
}
結果集包裝類(RemoteResult)
1 /**
2 * <pre>
3 * 遠端介面值物件,此物件使用說明
4 * 使用時,判斷isSuccess返回值,true表示業務成功、false表示介面呼叫失敗
5 * errorCode,用於判斷失敗原因(非系統錯誤),系統預設錯誤碼,用負數表示:-1表示引數不合法,使用者自定義錯誤碼使用正數表示,0表示無錯誤
6 * </pre>
7 *
8 * @author jx
9 * @param <T>
10 */
11
12
13 public class RemoteResult<T> implements Serializable {
14
15 private static final long serialVersionUID = 1L;
16 /** 介面呼叫是否成功(業務),系統錯誤、業務失敗都將返回false */
17 private boolean isSuccess = true;
18 /** 自定義錯誤資訊,發生可處理錯誤時,返回自定義資訊 */
19 private String errorMsg = "ok";
20 /** 介面返回結果(Void表示無返回值) */
21 private T result;
22 /** 異常堆疊資訊,需要提供除錯功能時,將異常加入此堆疊中,便於協調呼叫方除錯,僅作除錯用 */
23 private Exception exceptionStack;
24
25 public RemoteResult() {
26 }
27
28 public RemoteResult<T> error(String errorMsg) {
29 this.errorMsg = errorMsg;
30 this.isSuccess = false;
31 return this;
32 }
33
34 public static long getSerialVersionUID() {
35 return serialVersionUID;
36 }
37
38 public boolean isSuccess() {
39 return isSuccess;
40 }
41
42 public void setSuccess(boolean success) {
43 isSuccess = success;
44 }
45
46 public String getErrorMsg() {
47 return errorMsg;
48 }
49
50 public void setErrorMsg(String errorMsg) {
51 this.errorMsg = errorMsg;
52 }
53
54 public T getResult() {
55 return result;
56 }
57
58 public void setResult(T result) {
59 this.result = result;
60 }
61
62 public Exception getExceptionStack() {
63 return exceptionStack;
64 }
65
66 public void setExceptionStack(Exception exceptionStack) {
67 this.exceptionStack = exceptionStack;
68 }
69
70 }