Spring Boot中如何干掉if else

需求

這里虛擬一個業(yè)務需求荷并,讓大家容易理解豹障。假設有一個訂單系統(tǒng)巧涧,里面的一個功能是根據訂單的不同類型作出不同的處理膊存。
訂單實體:

public class OrderDTO {
    
    private String code;

    private BigDecimal price;

    /**
     * 訂單類型
     * 1:普通訂單;
     * 2:團購訂單禁灼;
     * 3:促銷訂單管挟;
     */
    private String type;
    
    // ... 省略 get / set ...
    
}

service接口:

public interface IOrderService {

    /**
     * 根據訂單的不同類型作出不同的處理
     *
     * @param dto 訂單實體
     * @return 為了簡單,返回字符串
     */
    String handle(OrderDTO dto);

}

傳統(tǒng)實現

根據訂單類型寫一堆的if else

public class OrderServiceImpl implements IOrderService {

    @Override
    public String handle(OrderDTO dto) {
        String type = dto.getType();
        if ("1".equals(type)) {
            return "處理普通訂單";
        } else if ("2".equals(type)) {
            return "處理團購訂單";
        } else if ("3".equals(type)) {
            return "處理促銷訂單";
        }
        return null;
    }

}

策略模式實現

利用策略模式弄捕,只需要兩行即可實現業(yè)務邏輯:

public class OrderServiceV2Impl implements IOrderService {

    @Autowired
    private HandlerContext handlerContext;

    @Override
    public String handle(OrderDTO dto) {
        AbstractHandler handler = handlerContext.getInstance(dto.getType());
        return handler.handle(dto);
    }

}

可以看到上面的方法中注入了HandlerContext僻孝,這是一個處理器上下文,用來保存不同的業(yè)務處理器守谓,具體在下文會講解穿铆。我們從中獲取一個抽象的處理器AbstractHandler,調用其方法實現業(yè)務邏輯斋荞。
現在可以了解到荞雏,我們主要的業(yè)務邏輯是在處理器中實現的,因此有多少個訂單類型平酿,就對應有多少個處理器凤优。以后需求變化,增加了訂單類型蜈彼,只需要添加相應的處理器就可以筑辨,上述OrderServiceV2Impl完全不需改動。
我們先看看業(yè)務處理器的寫法:

@Component
@HandlerType("1")
public class NormalHandler extends AbstractHandler {

    @Override
    public String handle(OrderDTO dto) {
        return "處理普通訂單";
    }

}
@HandlerType("2")
public class GroupHandler extends AbstractHandler {

    @Override
    public String handle(OrderDTO dto) {
        return "處理團購訂單";
    }

}
@HandlerType("3")
public class PromotionHandler extends AbstractHandler {

    @Override
    public String handle(OrderDTO dto) {
        return "處理促銷訂單";
    }

}

首先每個處理器都必須添加到spring容器中幸逆,因此需要加上@Component注解棍辕,其次需要加上一個自定義注解@HandlerType,用于標識該處理器對應哪個訂單類型还绘,最后就是繼承AbstractHandler楚昭,實現自己的業(yè)務邏輯。
自定義注解 @HandlerType

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandlerType {

    String value();

}

抽象處理器AbstractHandler

public abstract class AbstractHandler {
    abstract public String handle(OrderDTO dto);
}

自定義注解和抽象處理器都很簡單蚕甥,那么如何將處理器注冊到spring容器中呢哪替?
具體思路是:

  • 掃描指定包中標有@HandlerType的類;
  • 將注解中的類型值作為key菇怀,對應的類作為value,保存在Map中晌块;
  • 以上面的map作為構造函數參數爱沟,初始化
    HandlerContext,將其注冊到spring容器中匆背;

我們將核心的功能封裝在HandlerProcessor類中呼伸,完成上面的功能。
HandlerProcessor

@Component
@SuppressWarnings("unchecked")
public class HandlerProcessor implements BeanFactoryPostProcessor {

    private static final String HANDLER_PACKAGE = "com.cipher.handler_demo.handler.biz";

    /**
     * 掃描@HandlerType,初始化HandlerContext括享,將其注冊到spring容器
     *
     * @param beanFactory bean工廠
     * @see HandlerType
     * @see HandlerContext
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Map<String, Class> handlerMap = Maps.newHashMapWithExpectedSize(3);
        ClassScaner.scan(HANDLER_PACKAGE, HandlerType.class).forEach(clazz -> {
            // 獲取注解中的類型值
            String type = clazz.getAnnotation(HandlerType.class).value();
            // 將注解中的類型值作為key搂根,對應的類作為value,保存在Map中
            handlerMap.put(type, clazz);
        });
        // 初始化HandlerContext铃辖,將其注冊到spring容器中
        HandlerContext context = new HandlerContext(handlerMap);
        beanFactory.registerSingleton(HandlerContext.class.getName(), context);
    }

}

ClassScaner:掃描工具類源碼

HandlerProcessor需要實現BeanFactoryPostProcessor剩愧,在spring處理bean前,將自定義的bean注冊到容器中娇斩。
核心工作已經完成仁卷,現在看看HandlerContext如何獲取對應的處理器:
HandlerContext:

public class HandlerContext {

    private Map<String, Class> handlerMap;

    public HandlerContext(Map<String, Class> handlerMap) {
        this.handlerMap = handlerMap;
    }

    public AbstractHandler getInstance(String type) {
        Class clazz = handlerMap.get(type);
        if (clazz == null) {
            throw new IllegalArgumentException("not found handler for type: " + type);
        }
        return (AbstractHandler) BeanTool.getBean(clazz);
    }

}

BeanTool:獲取bean工具類
#getInstance方法根據類型獲取對應的class,然后根據class類型獲取注冊到spring中的bean犬第。
最后請注意一點锦积,HandlerProcessorBeanTool必須能被掃描到,或者通過@Bean的方式顯式的注冊歉嗓,才能在項目啟動時發(fā)揮作用丰介。

歡迎大家加入粉絲群:963944895,群內免費分享Spring框架鉴分、Mybatis框架SpringBoot框架哮幢、SpringMVC框架、SpringCloud微服務冠场、Dubbo框架家浇、Redis緩存、RabbitMq消息碴裙、JVM調優(yōu)钢悲、Tomcat容器、MySQL數據庫教學視頻及架構學習思維導圖

總結

利用策略模式可以簡化繁雜的if else代碼舔株,方便維護莺琳,而利用自定義注解和自注冊的方式,可以方便應對需求的變更载慈。本文只是提供一個大致的思路惭等,還有很多細節(jié)可以靈活變化,例如使用枚舉類型办铡、或者靜態(tài)常量辞做,作為訂單的類型,相信你能想到更多更好的方法寡具。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末秤茅,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子童叠,更是在濱河造成了極大的恐慌框喳,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異五垮,居然都是意外死亡乍惊,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門放仗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來润绎,“玉大人,你說我怎么就攤上這事匙监》渤鳎” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵亭姥,是天一觀的道長稼钩。 經常有香客問我,道長达罗,這世上最難降的妖魔是什么坝撑? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮粮揉,結果婚禮上巡李,老公的妹妹穿的比我還像新娘。我一直安慰自己扶认,他們只是感情好侨拦,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辐宾,像睡著了一般狱从。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叠纹,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天季研,我揣著相機與錄音,去河邊找鬼誉察。 笑死与涡,一個胖子當著我的面吹牛,可吹牛的內容都是我干的持偏。 我是一名探鬼主播驼卖,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸿秆!你這毒婦竟也來了款慨?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谬莹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體附帽,經...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡埠戳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了蕉扮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片整胃。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖喳钟,靈堂內的尸體忽然破棺而出屁使,到底是詐尸還是另有隱情,我是刑警寧澤奔则,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布蛮寂,位于F島的核電站,受9級特大地震影響易茬,放射性物質發(fā)生泄漏酬蹋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一抽莱、第九天 我趴在偏房一處隱蔽的房頂上張望范抓。 院中可真熱鬧,春花似錦食铐、人聲如沸匕垫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽象泵。三九已至,卻和暖如春铃慷,著一層夾襖步出監(jiān)牢的瞬間单芜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工犁柜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洲鸠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓馋缅,卻偏偏與公主長得像扒腕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子萤悴,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內容

  • http://liuxing.info/2017/06/30/Spring%20AMQP%E4%B8%AD%E6%...
    sherlock_6981閱讀 15,940評論 2 11
  • 什么是Spring Spring是一個開源的Java EE開發(fā)框架瘾腰。Spring框架的核心功能可以應用在任何Jav...
    jemmm閱讀 16,475評論 1 133
  • 本文是我自己在秋招復習時的讀書筆記,整理的知識點覆履,也是為了防止忘記蹋盆,尊重勞動成果费薄,轉載注明出處哦!如果你也喜歡栖雾,那...
    波波波先森閱讀 12,297評論 6 86
  • Everything went on as usual.It occurred me that there was...
    chuxinyu閱讀 229評論 0 0
  • 也是和上一本同系列推薦的小說楞抡,怕和《裂舌》一樣坑所以先去豆瓣瞅了一眼∥雠海看評論感覺還行吧召廷,總之就是一部不能當正常推理...
    Super_亭小亭閱讀 119評論 0 0