java策略模式去掉冗餘if-else支付案例實戰
前言
上一篇文章介紹了 java中冗餘if-else代碼塊的優化(策略模式法,反射法)
,裏面詳細介紹了策略模式+工廠模式來解決代碼中大量的if-else結構。
這一篇就舉個實戰例來說明,怎麼使用策略模式。
1.支付案例需求
假設你在做的某個項目,需求就是頁面選擇不同的付款渠道,計算不同的折扣,進行扣款。
看到這個圖片,有小夥伴在遇到諸如此類的需求的時候,想到的會是,很簡單,if else 判段下就可以了嘛,if else 確實可以解決我們的問題,但是弊端也很明顯:
- 代碼冗長
- 維護不便
誰叫我們學習過了策略模式+工廠模式來解決代碼中大量的if-else結構呢?
2.策略設計模式幹掉if-else(通用寫法)
策略模式+工廠模式解決代碼中大量的if-else結構的(通用寫法),以後遇到多條件的業務需求,就直接可以複用了。
文件目錄:
1.定義充值支付的枚舉類
RechargeTypeEnum.java 源碼如下:
package PayDemo;
/**
* @Description: 1.定義枚舉類型
* @Author: Zoutao
* @Date: 2020/4/10
*/
public enum RechargeTypeEnum {
/**
* 枚舉類型,根據業務需求增刪
*/
ABC_BANK(1,"農業銀行"),
ICBC_BANK(2,"工商銀行"),
/*根據自己到底業務需求增加個數*/
CCB_BANK(3,"建設銀行"),
CMB_BANK(4,"招商銀行"),;
/**
* 狀態值
*/
private int code;
/**
* 類型描述
*/
private String description;
RechargeTypeEnum(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
// 根據code獲取對應的枚舉類型
public static RechargeTypeEnum getPayTypeEnum(int code) {
for (RechargeTypeEnum calcTypeEnum : RechargeTypeEnum.values()) {
if (calcTypeEnum.getCode() == code) {
return calcTypeEnum;
}
}
System.out.println("對應支付計算策略不存在,[type={\"+code+\"}]");
return null;
}
}
2.定義支付的計算策略接口類
PayStrategy.java:
package PayDemo;
/**
* @Description: 2.定義支付的策略接口類
* @Author: Zoutao
* @Date: 2020/4/10
*/
public interface PayStrategy {
/**
* 策略行爲方法:定義根據金額進行結算的方法
* @param charge
* @param type
* @return
*/
public Double calRecharge(Double charge ,RechargeTypeEnum type);
}
3.定義多個結算渠道具體的結算實現類
創建實現類的數量視自己的業務需求而定。
(針對每一類情況來分別實現上面接口,不是繼承,是實現!!!
所以要在實現的方法中寫好具體的計算規則代碼。)
ABCPayStrategy.java
ICBCPayStrategy.java
CCBPayStrategy.java
…
比如我們寫一個農業銀行的,一個工商銀行的。
ABCPayStrategy.java代碼如下:
package PayDemo;
/**
* @Description: 具體有多少業務實現類,視自身業務而定
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class ABCPayStrategy implements PayStrategy {
/**
* 農業銀行的結算實現類
* 優惠98折
* @param charge 金額
* @param type 支付類型
* @return
*/
@Override
public Double calRecharge(Double charge, RechargeTypeEnum type) {
return charge*0.98;
}
}
ICBCPayStrategy.java代碼如下:
package PayDemo;
/**
* @Description:
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class ICBCPayStrategy implements PayStrategy{
/**
* 這邊就是具體業務代碼的實現方法
* 工商銀行的結算實現類
* 收取5元手續費
* @return
*/
@Override
public Double calRecharge(Double charge, RechargeTypeEnum type) {
return charge+5;
}
}
…
實現類的數量視自己業務而定。
4.策略上下文類PayContext.java
作用就是根據類型路由到具體的實現類。
其中,創建接口的對象。
調用工廠類的getInstance和creator方法來路由到具體實現方法中。
package PayDemo;
/**
* @Description: 4.策略上下文,作用就是根據類型路由到具體的實現類
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class PayContext {
// 創建接口的對象
private PayStrategy strategy;
public Double calRecharge(Double charge, Integer type) {
try{
strategy= StrategyFactory.getInstance().creator(type); // 調用工廠類的getInstance和creator方法
}catch (Exception e){
//log
System.out.println("程序出錯了!");
}
return strategy.calRecharge(charge,RechargeTypeEnum.getPayTypeEnum(type));
}
public PayStrategy getStrategy() {
return strategy;
}
public void setStrategy(PayStrategy strategy) {
this.strategy = strategy;
}
}
5.建立工程模式類StrategyFactory.java,
工廠模式來批量創建我們需要的類型的對象。new ABCPayStrategy(),new ICBCPayStrategy()。。。
package PayDemo;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 工廠模式-根據類型,把具體的實現類對象創建出來
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class StrategyFactory {
//生成工廠對象
private static StrategyFactory factory = new StrategyFactory();
private StrategyFactory(){
}
private static Map<Integer ,PayStrategy> strategyMap = new HashMap<>();
static{
//通過put或註解的方式把對象加載到map中
strategyMap.put(RechargeTypeEnum.ABC_BANK.getCode(), new ABCPayStrategy());
strategyMap.put(RechargeTypeEnum.ICBC_BANK.getCode(), new ICBCPayStrategy());
strategyMap.put(RechargeTypeEnum.CCB_BANK.getCode(), new CCBPayStrategy());
/* 自行增加業務需求
strategyMap.put(RechargeTypeEnum.CMB_BANK.getCode(), new CardStrategy());...
*/
}
//工廠模式來創建我們需要的對象
public PayStrategy creator(Integer type){
return strategyMap.get(type);
}
//返回工廠對象
public static StrategyFactory getInstance(){
return factory;
}
}
6.接口的測試類PayTest.java,
必須創建上下文對象,調用接口方法calRecharge計算策略,得到對應的需求結果。
package PayDemo;
/**
* @Description: 上下文對象來調用對應的支付方式測試
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class PayTest {
public static void main(String[] args) {
// 創建上下文對象來調用對應的支付方式
PayContext context = new PayContext();
// 農業充值100 需要付多少
Double money = context.calRecharge(100D,
RechargeTypeEnum.ABC_BANK.getCode());
System.out.println(money);
// 工商充值100 需要付多少
Double money2 = context.calRecharge(100D,
RechargeTypeEnum.ICBC_BANK.getCode());
System.out.println(money2);
// 新增-建設銀行充值方式
Double money3 = context.calRecharge(100D,
RechargeTypeEnum.CCB_BANK.getCode());
System.out.println(money3);
}
}
以上就完成了一個策略模式+工廠模式的通用代碼結構,在文章解決掉了所有的if-else結構,優化了代碼,有利於後期的維護。
7.如果要新增一個新功能業務?
–以新建一個建設銀行支付渠道爲例。
(只需要新增代碼而不需要修改原代碼。這樣設計程序的好處就體現出來了。)
a.首先在枚舉類RechargeTypeEnum中新增類型和描述。
CCB_BANK(3,“建設銀行”),
b.在增一個對應接口實現類CCBPayStrategy。
定義具體的接口方法內容。
package PayDemo;
public class CCBPayStrategy implements PayStrategy {
/**
* 建設銀行實現類
* 優惠96折
* @param charge
* @param type
* @return
*/
@Override
public Double calRecharge(Double charge, RechargeTypeEnum type) {
return charge * 0.96;
}
}
c.然後在工廠類StrategyFactory當中添加,生成對應的類對象和枚舉類型。
strategyMap.put(RechargeTypeEnum.CCB_BANK.getCode(), new CCBPayStrategy());
d.最終在PayTest調用即可。
// 新增-建設銀行充值方式
Double money3 = context.calRecharge(100D,
RechargeTypeEnum.CCB_BANK.getCode());
System.out.println(money3);
結果:
至此我們業務功能就完成了,可能大家會覺得,設計模式的引入反而造成了類太多,還不如直接if else寫在一個文件裏的直觀。
但是,對於後期的程序開發中,如果要新增一個付款渠道,使用策略模式的方式將變得很簡單,只要定義一個具體的實現類,加上對應的工廠對象調用就解決了,根本不需要去閱讀之前別人寫的其他支付渠道的代碼,松耦合的目的就在於此。
參考地址:
註解版:https://zhuanlan.zhihu.com/p/33383648
https://www.iteye.com/blog/alaric-1920714
https://blog.csdn.net/u014395955/article/details/103804479
https://www.toutiao.com/i6813584351410782723/