不吹牛X,我真的干掉了if-else

新手發(fā)文,在線求關(guān)注

我們在web開發(fā)中,經(jīng)常使用數(shù)據(jù)庫表中的字段作為“標記”來表示多個“狀態(tài)”,比如:
我們就以某寶的在線購物流程為例進行分析。在訂單表中,使用zt字段來表示訂單的狀態(tài),常見的狀態(tài)就有:
= 狀態(tài)碼表示?=
0
待付款
1
待發(fā)貨
2
待收貨
3
待評價
4
售后
當我們想按條件查詢各個類型的訂單的時候,只需要一個接口,在前端傳入相應(yīng)的狀態(tài)碼就可以了。在dao層大概也就是通過如下的語句進行查詢:
select?* from?orders where?zt = #{zt}如何才能有很高的擴展性?
假設(shè)有這么幾個“不成需求的需求”:
我想讓待收貨的訂單按照訂單發(fā)貨時間或者預計送達時間排序,其他的暫且按照訂單創(chuàng)建時間排序吧
想將“待收貨”的狀態(tài)區(qū)分開,分為“用戶未收到貨”和“用戶收到貨但是未點擊確認收貨按鈕”兩種狀態(tài)
常規(guī)方式如何解決?
需求一(不同的狀態(tài)處理方式不同):
這個很容易的,在sevice層添加一個判斷就可以,其他的代碼不用改,代碼如下:
// 2 表示待收貨
if(zt == 2){
?//按照需求,按照訂單發(fā)貨時間或者預計送達時間排序
}else{
?//其他狀態(tài)的訂單,全部按照訂單創(chuàng)建時間排序
}上邊這個代碼的修改量已經(jīng)很小了,但是如果我要把各種不同的狀態(tài)訂單全部按照不同的排序方式排序呢?你可能會寫如下代碼
if(zt==0){
?// 待付款的訂單處理代碼...
}else?if(zt==1){
?
}else?if(zt==2){
?
}else?if(zt==3){
?
}else?if(zt==4){
?
}上邊代碼太low了,有些小伙伴可能會使用switch進行優(yōu)化(這里就不寫代碼了,因為和上邊并沒有任何區(qū)別)。
需求二(添加一個新的狀態(tài)表示):
這也很easy啊,直接在上邊的if-else或者switch代碼中添加新的狀態(tài)判斷不就好了。
思考如何干掉if-else?
上邊的方式可以完成我們的需求,但是有以下幾點不足:
面對“各種各樣奇怪的需求”,我們要頻繁地修改上邊的代碼,時間久了,豈不成了渣渣。甚至我們自己都不愿意再去看這些代碼了;
如果新增加一個狀態(tài)表示,也就是給zt字段新的狀態(tài)含義表示,我們又要添加if-else,這太復雜了。
使用策略模式來解決if-else的問題
是的,就是使用策略模式來解決進行太多的狀態(tài)判斷代碼就是一個好辦法。比如,就上邊每一個if-else中的代碼抽成一個類或者方法進行處理。
主要的代碼我就不寫了,因為下邊才是我們的主菜,這里說的這種方式只能解決if-else里邊的代碼復雜問題,將代碼進行一定程度上的解耦。但并沒有實質(zhì)地解決if-else的問題,而且這也是網(wǎng)上大多數(shù)的解決辦法。
如果對策略模式不太了解的小伙伴,可以看下這篇文章,不看也沒關(guān)系,在下邊你會看到怎么用的。
嘗試使用Spring來配合策略模式
程序設(shè)計的一大原則“對擴展開放,對修改關(guān)閉”,定義一個接口類,用來查詢不同狀態(tài)的訂單列表。如下:
public?interface?OrderService?{
???/**
????* 查詢對應(yīng)狀態(tài)的訂單列表
????* @param?zt
????* @return
????*/
???List<Order> getOrderList(String zt);
}然后根據(jù)不同的訂單狀態(tài)創(chuàng)建不同的實現(xiàn)類,比如,“待付款”的訂單查詢類如下:
雖然以下的命名方式屬于錯誤示范,但是卻能很好地理解
@Service("orderServiceDfk") // 這個命名確實很不友好,但是我相信你能理解哈
public?class?PendingParymentOrderSeviceImpl?implements?OrderService?{
???/**
????* 查詢待付款的訂單列表
????*
????* @param?zt
????* @return
????*/
???@Override
???public?List<Order> getOrderList(String zt)?{
?????//這里要利用dao層從數(shù)據(jù)庫中查詢出來相應(yīng)的訂單列表
?????return?null;
??}看了這兩個類的代碼,我相信小伙伴們應(yīng)該能理解了要怎么做了,就是根據(jù)前端傳來不同的zt值,后臺使用不同的類來處理,但是我們可以通過Spring來完全取掉if-else。
我們的controller層代碼如下:
@RestController
public?class?OrderController {
?private?String?orderServiceBeanNamePrefix = "orderService";
?@RequestMapping("getOrderList/{zt}")
?public?List<Order> getOrderList(@PathVariable("zt") String?zt) {
???//獲取對應(yīng)的處理狀態(tài)的bean來處理
???//就通過這樣一句代碼,完全解決了if-else的判斷邏輯
???OrderService orderService = (OrderService) SpingContext.getBean(orderServiceBeanNamePrefix + zt);
???List<Order> orderList = orderService.getOrderList();
???return?orderList;
}
}上邊用了一個工具類,就是從Spring 容器中獲取相應(yīng)的bean,代碼如下:
/**
* 微信公眾號 “小魚與Java”
*
* 原理很簡單,我們寫的類實現(xiàn)這個接口,具體可以查閱Spring生命周期相關(guān)內(nèi)容
* Spring會自動調(diào)用其中的setApplicationContext方法,傳入Spring容器上下文
* 我們就在這里把Spring上下文保存下來
*
* @date?2020/5/18
* @auther?Lyn4ever
*/
@Component
public?class?SpingContext?implements?ApplicationContextAware?{
???private?static?ApplicationContext applicationContext;
???/**
????* 根據(jù)name從Spring容器中獲取bean
????* @param?name
????* @return
????*/
???public?static?Object getBean(String name){
???????return?applicationContext.getBean(name);
??}
???@Override
???public?void?setApplicationContext(ApplicationContext applicationContext)?throws?BeansException {
???????System.out.println("我保存了Spring上下文");
???????applicationContext = applicationContext;
??}
}總結(jié):
解決if-else的思路就是使用策略模式,針對不同“狀態(tài)”的訂單,使用不同的類來處理邏輯,這樣就可以很好地進行了“解耦”操作。但是,如果新增一個“狀態(tài)表示 ”,我們就要在主邏輯處添加if-else進行判斷要用哪個類來處理。
而解決這個“判斷 ”的中使用的if-else就有很多方法:抽象工廠也是一個不錯的方法。而我們使用Spring的控制反轉(zhuǎn)同樣也可以很好地解決這個問題。這么做的好處如下:
“真正的”解決了與我們業(yè)務(wù)無關(guān)的if-else;
不用前后端再進行狀態(tài)的表示“約定”,之前用0表示“待付款”,1表示 “待發(fā)貨”這樣的操作,如果記錯,那一定會有大問題?,F(xiàn)在,使用特定的字符串來表示,也就是說,前端直接傳入想要解決這個方案對應(yīng)的bean,從而少去了“復雜且易出錯的約定”環(huán)節(jié)。

點擊“閱讀原文”獲取代碼吧
