場景
此時(shí)有一個(gè)場景,需要設(shè)計(jì)一個(gè)根據(jù)不同的狀態(tài)和條件采用不同的業(yè)務(wù)處理方式。
這樣大家可能不是太理解。舉個(gè)例子,現(xiàn)在大街小巷上的商戶都采用了聚合支付的支付方式,聚合支付也就是商戶柜臺(tái)前放了一個(gè)支持支付寶、微信、京東錢包、銀聯(lián)等等的二維碼,用戶可以通過任意一款支付APP進(jìn)行支付。
解決思路
思路①
對每個(gè)支付渠道進(jìn)行定義枚舉類型
public enum PayWay {
ALI_PAY,
WECHAT_PAY;
}
然后在每個(gè)對應(yīng)的service上定義注解,表示對應(yīng)哪種支付方式
@Pay(PayWay.ALI_PAY)
public class AliPayServiceImpl implements PayService {}
但是仔細(xì)思考后,還是存在一些問題
- 如果增加一個(gè)支付方式后還需要修改,
PayWay這個(gè)枚舉類型 - 在程序中,仍需要根據(jù)不同的條件做
if else判斷PayWay,增加支付方式還是得修改原有的判斷邏輯。偽代碼如下
if("xxx" == "aliPay"){
} else if("xxx" == "wechatPay"){
}
//如果增加支付方式還是得增加else if
思路②
在思路①中存在一些問題,首當(dāng)其沖的就是if else判斷問題。先思考一下這個(gè)if else的作用是什么?
答:根據(jù)思路①描述,這個(gè)if else是用來確定采用哪種支付方式。
我們可以將這塊代碼抽離出來,讓對應(yīng)的業(yè)務(wù)實(shí)現(xiàn)類實(shí)現(xiàn)自己的邏輯實(shí)現(xiàn),然后根據(jù)返回值true 或者false決定是否過濾掉這個(gè)業(yè)務(wù)實(shí)現(xiàn)類。接口定義如下,SupportBean是封裝的一個(gè)實(shí)體
boolean isSupport(SupportBean supportBean);
然后在各個(gè)業(yè)務(wù)實(shí)現(xiàn)類都實(shí)現(xiàn)自己的isSupport方法,偽代碼如下
@Override
public boolean isSupport(SupportBean supportBean) {
if (supportBean.getType() == "xxx"){
return true;
}
return false;
}
設(shè)計(jì)
注:只提供一個(gè)架子
接口定義
Service接口定義,一個(gè)業(yè)務(wù)執(zhí)行方法execute(參數(shù)自行添加),一個(gè)isSupport方法(返回true或者false)
public interface Service {
void execute();
boolean isSupport(SupportBean supportBean);
}
業(yè)務(wù)實(shí)現(xiàn)類
這里execute方法只是在控制臺(tái)打印字符串。isSupport方法對SupportBean中的supportNum進(jìn)行取余,判斷余數(shù)是否等于0,是則返回true。
類似的實(shí)現(xiàn)還有兩個(gè),這里就不貼出來了。
@Component
public class AServiceImpl implements Service {
@Override
public void execute() {
System.out.println("A execute");
}
@Override
public boolean isSupport(SupportBean supportBean) {
return supportBean.getSupportNum() % 3 == 0;
}
}
接下來在定義一個(gè)幫助類
幫助類
@Component
public class Helper {
@Autowired
private List<Service> services;
public void execute(SupportBean supportBean){
Service s = services.stream()
.filter((service) -> service.isSupport(supportBean))
.findFirst()//NPE異常
.orElse(null);
if (s != null){
s.execute();
}
}
}
通過工具類的execute方法來獲取對應(yīng)的業(yè)務(wù)實(shí)現(xiàn)類執(zhí)行的結(jié)果,以及對傳入的參數(shù)進(jìn)行校驗(yàn)處理等。
需要注意的是Lambda表達(dá)式的findFirst()會(huì)出現(xiàn)NullPointException異常。因?yàn)閒ilter對list進(jìn)行過濾,會(huì)存在過濾完list的長度為0,如果此時(shí)在調(diào)用findFirst則會(huì)拋出NullPointException??梢詫⑸厦娴拇a修改為如下代碼,這樣就可以避免NPE了
Service s = services.stream()
.filter((service) -> service.isSupport(supportBean))
.map(Optional::ofNullable)
.findFirst()
.flatMap(Function.identity())
.orElse(null);
測試
添加一個(gè)springboot測試類和一個(gè)測試方法。
在contextLoads測試中調(diào)用幫助類Helper的execute方法
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Autowired
private Helper Helper;
@Test
public void contextLoads() {
Helper.execute(new SupportBean(3));
}
}
測試結(jié)果
A execute
擴(kuò)展
在Lambda表達(dá)式中是先將業(yè)務(wù)實(shí)現(xiàn)類進(jìn)行過濾,然后獲取第一個(gè)業(yè)務(wù)實(shí)現(xiàn)類并執(zhí)行。
如果此時(shí)過濾存在多個(gè)業(yè)務(wù)實(shí)現(xiàn)類,而又不能確定優(yōu)先級,這時(shí)需要如何進(jìn)行擴(kuò)展呢?
其實(shí)很簡單,先在Service接口中定義一個(gè)getPriority方法
int getPriority();
然后各自的實(shí)現(xiàn)類實(shí)現(xiàn)對應(yīng)的getPriority方法
接著修改Lambda表達(dá)式即可,在filter后增加sorted方法即可對業(yè)務(wù)實(shí)現(xiàn)類進(jìn)行排序
Service s = services.stream()
.filter((service) -> service.isSupport(supportBean))
.sorted(Comparator.comparing(Service::getPriority))
.map(Optional::ofNullable)
.findFirst()
.flatMap(Function.identity())
.orElse(null);
總結(jié)
整個(gè)大體框架基本都搭建完成,如需擴(kuò)展只需要增加對應(yīng)的業(yè)務(wù)實(shí)現(xiàn)類,而不用去修改其他類的代碼。就連之前設(shè)計(jì)的枚舉都可以不用,可擴(kuò)展性大大提升。如需使用,只需修改對應(yīng)的入?yún)⒑蛯?yīng)的名稱即可。
Github地址
如果對你有收獲,歡迎star、歡迎fork
如果你也有類似的經(jīng)驗(yàn),歡迎加入,一起共建