1 背景
某天早上,Skr郎正在一邊悠哉的吃著加了三個雞蛋的手抓餅,一邊悠閑地逛著論壇,看著沙雕網(wǎng)友的帖子,Skr郎會心一笑,正欲給沙雕帖子點贊,郵件忽的彈出,Skr郎慢悠悠的打開郵件
任務: 對接阿里支付接口
時限: 一周
要求如下:
1. 單筆限額不超過1W
2. 可以成功提現(xiàn)
3. 發(fā)起提現(xiàn)后,可以主動查詢提現(xiàn)結果
Skr郎心中暗喜,看我兩天搞定,剩下的時間就可以嘿嘿嘿了
/**
* 阿里業(yè)務處理
*
* @author gp6
* @date 2020/3/13
*/
public class AlibabaHandler {
void singleQuota() {
System.out.println("阿里校驗單筆限額");
}
void pay() {
System.out.println("阿里支付邏輯");
}
void getResult() {
System.out.println("阿里主動查詢支付結果");
}
}
/**
* 支付服務類
*
* @author gp6
* @date 2020/3/13
*/
public class PayService {
public static void main(String[] args) {
// 支付接口(第一個方法)
AlibabaHandler alibabaHandler = new AlibabaHandler();
alibabaHandler.singleQuota();
alibabaHandler.pay();
// 查詢接口(第二個方法)
AlibabaHandler alibabaHandler1 = new AlibabaHandler();
alibabaHandler1.getResult();
}
}
Skr郎看著完成的代碼,心中評價道:這段代碼,邏輯嚴謹,注釋清晰,優(yōu)雅中透露著灑脫,灑脫中透露著不羈,完美!
正洋洋自得間,項目經(jīng)理喊道:Skr郎,你過來一下!Skr郎趕緊屁顛屁顛的跑過去.
項目經(jīng)理:臨時加個需求,把微信支付也接進來,根據(jù)前臺傳的渠道,使用不同的支付方式,時間不加.
Skr郎:好的,沒問題(MMP)
/**
* 微信業(yè)務處理
*
* @author gp6
* @date 2020/3/13
*/
public class WechatHandler {
void singleQuota() {
System.out.println("微信校驗單筆限額");
}
void pay() {
System.out.println("微信支付邏輯");
}
void getResult() {
System.out.println("微信主動查詢支付結果");
}
}
/**
* 支付服務類
*
* @author gp6
* @date 2020/3/13
*/
public class PayService {
public static void main(String[] args) {
// 前臺傳的渠道 1:阿里 2:微信
String channel = "1";
// 支付邏輯(第一個方法)
if ("1".equals(channel)) {
// 阿里支付接口
AlibabaHandler alibabaHandler = new AlibabaHandler();
alibabaHandler.singleQuota();
alibabaHandler.pay();
} else if ("2".equals(channel)) {
// 微信支付接口
WechatHandler wechatHandler = new WechatHandler();
wechatHandler.singleQuota();
wechatHandler.pay();
}
// 查詢接口(第二個方法)
if ("1".equals(channel)) {
// 阿里查詢
AlibabaHandler alibabaHandler = new AlibabaHandler();
alibabaHandler.getResult();
} else if ("2".equals(channel)) {
// 微信查詢
WechatHandler wechatHandler = new WechatHandler();
wechatHandler.getResult();
}
}
}
Skr郎看著完成的代碼,心中評價道:這段代碼,邏輯嚴謹,注釋清晰,優(yōu)雅中透露著灑脫,灑脫中透露著不羈,雖然沒有上一版優(yōu)雅,但還是比較不錯的!
正洋洋自得間,又聽項目經(jīng)理喊道:Skr郎,你過來一下!Skr郎心里想著如果這次加需求不加時間,老子堅決不做!
項目經(jīng)理:臨時在加個需求,把通聯(lián)和Ping++支付也接進來,根據(jù)前臺傳的渠道,使用不同的支付方式,時間不加.
Skr郎:好的,沒問題(心里問候一下他的族中長輩)
/**
* Ping++業(yè)務處理
*
* @author gp6
* @date 2020/3/13
*/
public class PingHandler {
void singleQuota() {
System.out.println("Ping++校驗單筆限額");
}
void pay() {
System.out.println("Ping++支付邏輯");
}
void getResult() {
System.out.println("Ping++主動查詢支付結果");
}
}
/**
* 通聯(lián)業(yè)務處理
*
* @author gp6
* @date 2020/3/13
*/
public class AllinpayHandler {
void singleQuota() {
System.out.println("通聯(lián)校驗單筆限額");
}
void pay() {
System.out.println("通聯(lián)支付邏輯");
}
void getResult() {
System.out.println("通聯(lián)主動查詢支付結果");
}
}
/**
* 支付服務類
*
* @author gp6
* @date 2020/3/13
*/
public class PayService {
public static void main(String[] args) {
// 前臺傳的渠道 1:阿里 2:微信 3:Ping++ 4:通聯(lián)
String channel = "1";
// 支付邏輯(第一個方法)
if ("1".equals(channel)) {
// 阿里支付接口
AlibabaHandler alibabaHandler = new AlibabaHandler();
alibabaHandler.singleQuota();
alibabaHandler.pay();
} else if ("2".equals(channel)) {
// 微信支付接口
WechatHandler wechatHandler = new WechatHandler();
wechatHandler.singleQuota();
wechatHandler.pay();
}else if ("3".equals(channel)) {
// Ping++支付接口
PingHandler pingHandler = new PingHandler();
pingHandler.singleQuota();
pingHandler.pay();
}else if ("4".equals(channel)) {
// 通聯(lián)支付接口
AllinpayHandler allinpayHandler = new AllinpayHandler();
allinpayHandler.singleQuota();
allinpayHandler.pay();
}
// 查詢接口(第二個方法)
if ("1".equals(channel)) {
// 阿里查詢
AlibabaHandler alibabaHandler = new AlibabaHandler();
alibabaHandler.getResult();
} else if ("2".equals(channel)) {
// 微信查詢
WechatHandler wechatHandler = new WechatHandler();
wechatHandler.getResult();
}else if ("3".equals(channel)) {
// Ping++查詢
PingHandler pingHandler = new PingHandler();
pingHandler.getResult();
}else if ("4".equals(channel)) {
// 通聯(lián)查詢
AllinpayHandler allinpayHandler = new AllinpayHandler();
allinpayHandler.getResult();
}
}
}
Skr郎看著完成的代碼,心中評價道:這段代碼,邏輯嚴謹,注釋清晰.....我呸,這寫的什么鬼東西!看著長長的if else,Skr郎陷入沉思.....
如果后面再加新的渠道,是不是每次都要改動PayService中的調用邏輯,而且如果有別的方法要用此段邏輯,if else 又要復制一遍
腦中過濾著以往所學的設計模式......突然,靈光一閃,策略模式
2 策略模式
定義:
定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶
看了策略模式的定義,是不是很符合這種場景
定義了一系列算法(支付邏輯),并將每個算法(支付邏輯)封裝起來,使它們可以相互替換(根據(jù)不同channel切換不同的支付邏輯),且算法的變化不會影響使用算法的客戶(PayService)
簡直完美,Skr郎沾沾自喜
/**
* 支付的抽象策略類
* 抽象策略(Strategy)類:定義了一個公共接口,各種不同的算法以不同的方式實現(xiàn)這個接口,環(huán)境角色使用這個接口調用不同的算法,一般使用接口或抽象類實現(xiàn)
*
* @author gp6
* @date 2020/3/13
*/
public interface PayHandler {
/**
* 單筆限額
*/
void singleQuota();
/**
* 支付
*/
void pay();
/**
* 提現(xiàn)結果查詢
*/
void getResult();
}
/**
* 微信業(yè)務處理
* 具體策略(Concrete Strategy)類:實現(xiàn)了抽象策略定義的接口,提供具體的算法實現(xiàn)
*
* @author gp6
* @date 2020/3/13
*/
public class WechatHandler implements PayHandler {
public void singleQuota() {
System.out.println("微信校驗單筆限額");
}
public void pay() {
System.out.println("微信支付邏輯");
}
public void getResult() {
System.out.println("微信主動查詢支付結果");
}
}
/**
* 阿里業(yè)務處理
* 具體策略(Concrete Strategy)類:實現(xiàn)了抽象策略定義的接口,提供具體的算法實現(xiàn)
*
* @author gp6
* @date 2020/3/13
*/
public class AlibabaHandler implements PayHandler {
public void singleQuota() {
System.out.println("阿里校驗單筆限額");
}
public void pay() {
System.out.println("阿里支付邏輯");
}
public void getResult() {
System.out.println("阿里主動查詢支付結果");
}
}
通聯(lián)與Ping++代碼類似
/**
* 環(huán)境(Context)類:持有一個策略類的引用,最終給客戶端調用
*
* @author gp6
* @date 2020/3/13
*/
public class Context {
private PayHandler payHandler;
public Context(PayHandler payHandler){
this.payHandler = payHandler;
}
/**
* 單筆限額
*/
void singleQuota() {
payHandler.singleQuota();
}
/**
* 支付
*/
void pay() {
payHandler.pay();
}
/**
* 提現(xiàn)結果查詢
*/
void getResult() {
payHandler.getResult();
}
}
/**
* 支付服務類
*
* @author gp6
* @date 2020/3/13
*/
public class PayService {
public static void main(String[] args) {
// 前臺傳的渠道 1:阿里 2:微信 3:Ping++ 4:通聯(lián)
Integer channel = 1;
// 支付邏輯(第一個方法)
Context context;
switch (channel) {
case 1:
context = new Context(new AlibabaHandler());
break;
case 2:
context = new Context(new WechatHandler());
break;
case 3:
context = new Context(new PingHandler());
break;
case 4:
context = new Context(new AllinpayHandler());
break;
default:
throw new RuntimeException("渠道不支持!");
}
context.singleQuota();
context.pay();
// 查詢接口(第二個方法)
Context context2;
switch (channel) {
case 1:
context2 = new Context(new AlibabaHandler());
break;
case 2:
context2 = new Context(new WechatHandler());
break;
case 3:
context2 = new Context(new PingHandler());
break;
case 4:
context2 = new Context(new AllinpayHandler());
break;
default:
throw new RuntimeException("渠道不支持!");
}
context2.getResult();
}
}
使用了策略模式,但是調用者(PayService)中,還是有重復的大量的if else
根據(jù)不用的渠道創(chuàng)建不同的具體策略類(new AlibabaHandler()),這不是典型的工廠模式嗎.....
3 策略模式結合工廠模式
將工廠模式放入策略環(huán)境中
/**
* 環(huán)境(Context)類:持有一個策略類的引用,最終給客戶端調用
*
* @author gp6
* @date 2020/3/13
*/
public class Context {
private PayHandler payHandler;
public Context(Integer channel){
switch (channel) {
case 1:
payHandler = new AlibabaHandler();
break;
case 2:
payHandler = new WechatHandler();
break;
case 3:
payHandler = new PingHandler();
break;
case 4:
payHandler = new AllinpayHandler();
break;
default:
throw new RuntimeException("渠道不支持!");
}
}
/**
* 單筆限額
*/
void singleQuota() {
payHandler.singleQuota();
}
/**
* 支付
*/
void pay() {
payHandler.pay();
}
/**
* 提現(xiàn)結果查詢
*/
void getResult() {
payHandler.getResult();
}
}
/**
* 支付服務類
*
* @author gp6
* @date 2020/3/13
*/
public class PayService {
public static void main(String[] args) {
// 前臺傳的渠道 1:阿里 2:微信 3:Ping++ 4:通聯(lián)
Integer channel = 1;
// 支付邏輯(第一個方法)
Context context = new Context(channel);
context.singleQuota();
context.pay();
// 查詢接口(第二個方法)
Context context2 = new Context(channel);
context2.getResult();
}
}
此時如果再添加渠道,只需在Context的構造方法的判斷邏輯中添加新的渠道,然后在相應的支付邏輯類中寫入相應邏輯,只在策略環(huán)境中存在if else,調用者無需寫重復的if else
switch (channel) {
case 1:
payHandler = new AlibabaHandler();
break;
case 2:
payHandler = new WechatHandler();
break;
case 3:
payHandler = new PingHandler();
break;
case 4:
payHandler = new AllinpayHandler();
break;
// 新的渠道
case 5:
payHandler = new XXXHandler();
break;
default:
throw new RuntimeException("渠道不支持!");
}
/**
* 新渠道的業(yè)務邏輯處理
* 具體策略(Concrete Strategy)類:實現(xiàn)了抽象策略定義的接口,提供具體的算法實現(xiàn)
*
* @author gp6
* @date 2020/3/13
*/
public class XXXHandler implements PayHandler{
.....
}
寫到此處Skr郎欣喜的點點頭,暗自得意,正打算將代碼提交,忽然眉頭一皺,每次添加新的渠道,都要修改判斷邏輯,不是違背了 開閉原則 嗎
Skr再次沉思
4 使用反射遵循開閉原則
/**
* 環(huán)境(Context)類:持有一個策略類的引用,最終給客戶端調用
*
* @author gp6
* @date 2020/3/13
*/
public class Context {
private PayHandler payHandler;
public Context(PayEnum payEnum){
try {
payHandler= (PayHandler) Class.forName(payEnum.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException("渠道錯誤!");
}
}
/**
* 單筆限額
*/
void singleQuota() {
payHandler.singleQuota();
}
/**
* 支付
*/
void pay() {
payHandler.pay();
}
/**
* 提現(xiàn)結果查詢
*/
void getResult() {
payHandler.getResult();
}
}
/**
* 策略枚舉
*
* @author gp6
* @date 2020/3/13
*/
public enum PayEnum {
/**
* 渠道對應類全路徑
*/
OPERATION_ADD("com.gp6.pay.AlibabaHandler", 1),
OPERATION_SUB("com.gp6.pay.WechatHandler", 2),
OPERATION_MUL("com.gp6.pay.PingHandler", 3);
PayEnum(String className, Integer channel) {
this.className = className;
this.channel = channel;
}
/**
* 類的全路徑
*/
private String className;
/**
* 渠道
*/
private Integer channel;
/**
* 匹配渠道
*
* @param channel 渠道
* @return 相關枚舉
*/
public static PayEnum matchChannel(Integer channel) {
for (PayEnum payEnum : PayEnum.values()) {
if (payEnum.getChannel().equals(channel)) {
return payEnum;
}
}
throw new RuntimeException("渠道不支持!");
}
public String getClassName() {
return className;
}
public Integer getChannel() {
return channel;
}
}
/**
* 支付服務類
*
* @author gp6
* @date 2020/3/13
*/
public class PayService {
public static void main(String[] args) {
// 前臺傳的渠道 1:阿里 2:微信 3:Ping++ 4:通聯(lián)
Integer channel = 1;
// 支付邏輯(第一個方法)
Context context = new Context(PayEnum.matchChannel(channel));
context.singleQuota();
context.pay();
// 查詢接口(第二個方法)
Context context2 = new Context(PayEnum.matchChannel(channel));
context2.getResult();
}
}
此時,如果想添加通聯(lián)的渠道只需在PayEnum中添加
OPERATION_MUL("com.gp6.pay.AllinpayHandler", 4)
然后在com.gp6.pay.AllinpayHandler中撰寫相應邏輯即可
Skr郎寫到此處,心滿意足的點點頭,心中評價道:這段代碼,邏輯嚴謹,注釋清晰,優(yōu)雅中透露著灑脫,灑脫中透露著不羈,真是"此碼只應天上有,人間難得幾回尋!"
呀! 想不到我作詩的功夫也如此深厚,哎,如此完美的男人,別人只能羨慕嫉妒恨!
5 策略模式類圖
StrategyContext相當于文章中的Context
StrategyOperation相當于文章中的PayHandle
StrategyOperationAdd與StrategyOperationMul相當于文章中的AlibabaHandler/AllinpayHandler/PingHandler/WechatHandler