開發有個著名的設計原則:開閉原則,即對擴展開放,對修改關閉。可是實際開發中鮮有人能運用純熟,少俠在開發中接觸的例子就是,大多數人就是if...else...這樣難以擴展的條件判斷。那麼應該如何優雅的精簡掉複雜的邏輯判斷呢?固然抽象共性是從產品思惟角度的優化方案,今天少俠想說的是經過技術手段實現。java
首先簡單介紹一下業務背景,背景很簡單,就是有若干渠道源,如阿里巴巴,騰訊等,針對不一樣的渠道須要有不一樣的數據處理邏輯,而且渠道來源以後會不斷擴展。cookie
先創建一個簡單的枚舉類:app
/** * @author Carson * @date 2020/8/24 下午3:10 */ public enum SourceEnum { /** * 阿里巴巴 */ ALIBABA("ALIBABA"), /** * 騰訊 */ TENCENT("TENCENT"), ; public String name; SourceEnum(String name) { this.name = name; } //匹配 public static SourceEnum match(String name){ SourceEnum[] values = SourceEnum.values(); for (SourceEnum value : values) { if(value.name.equals(name)){ return value; } } return null; } public String getName() { return name; } }
再來看看業務層接口和具體的實現類(注意實現類上的@Service註解是指定的了別名的,方便以後利用 @Qualifier註解調用):ide
/** * @author Carson * @date 2020/8/24 下午3:10 */ public interface DataService { String dataProcess(); } /** * @author Carson * @date 2020/8/24 下午3:14 */ @Service("alibabaServiceImpl") public class AlibabaServiceImpl implements DataService { @Override public String dataProcess() { return "Alibaba Process++++++++++++++"; } } /** * @author Carson * @date 2020/8/24 下午3:12 */ @Service("tencentServiceImpl") public class TencentServiceImpl implements DataService { @Override public String dataProcess() { return "Tencent Process++++++++++++++"; } }
再來看看接口層的實現,主要是讓用戶傳入渠道源source(真實環境中這個多是從cookies裏的用戶信息獲取,這裏且不討論),而後根據不一樣的渠道進行判斷,注意看代碼裏的switch...case...語句,若是以後渠道擴展了,這裏勢必是要作改動的。有人可能以爲,看着還闊以啊,乾淨整潔,可是以後要是渠道不少呢,萬一百八十個渠道,這裏看着就很臃腫,而且萬一出錯排查起來也不方便,說得專業一點,就是擴展性很差。優化
/** * @author Carson * @date 20-8-20 下午5:00 */ @RestController public class CommonController { private final Logger logger = LoggerFactory.getLogger(CommonController.class); @Qualifier(value = "alibabaServiceImpl") @Autowired private AlibabaServiceImpl alibabaServiceImpl; @Qualifier(value = "tencentServiceImpl") @Autowired private TencentServiceImpl tencentServiceImpl; @GetMapping("/dataHandler") public String dataHandler(String source) { if (StringUtils.isBlank(source)) { return "Empty data"; } SourceEnum sourceEnum = SourceEnum.match(source); if (sourceEnum == null) { return "Empty data"; } switch (sourceEnum) { case ALIBABA: return alibabaServiceImpl.dataProcess(); case TENCENT: return tencentServiceImpl.dataProcess(); default: return "Empty data"; } } }
優化的第一步是從枚舉類開始,爲每一個枚舉類指定對應的Service實現類對象,注意,Spring中接口不能被加載成Bean實例,因此須要爲屬性和方法添加@Lookup
註解)。this
@Service public interface DataService { // 注意,此處必須加此@Lookup註解,不然容器沒法啓動 @Lookup String dataProcess(); }
public class AlibabaServiceImpl implements DataService { public AlibabaServiceImpl(){} @Override public String dataProcess() { return "Alibaba Process++++++++++++++"; } } public class TencentServiceImpl implements DataService { @Override public String dataProcess() { return "Tencent Process++++++++++++++"; } }
而後是改進後的枚舉類:設計
/** * @author Carson * @date 2020/8/24 下午3:10 */ public enum SourceEnum { /** * 阿里巴巴 */ ALIBABA("ALIBABA", new AlibabaServiceImpl()), /** * 騰訊 */ TENCENT("TENCENT", new TencentServiceImpl()), ; public String name; public DataService dataService; SourceEnum(String name, DataService dataService) { this.name = name; this.dataService = dataService; } //匹配 public static SourceEnum match(String name) { SourceEnum[] values = SourceEnum.values(); for (SourceEnum value : values) { if (value.name.equals(name)) { return value; } } return null; } public String getName() { return name; } public DataService getDataService() { return dataService; } }
而後接口層的改造以下,注意看這裏的註解只是配置了接口的自動裝配(並未註解全部的實現類),而後經過多態實現具體調用。code
@RestController public class CommonController { private final Logger logger = LoggerFactory.getLogger(CommonController.class); @Autowired private DataService dataService; @GetMapping("/dataHandler") public String dataHandler(String source) { if (StringUtils.isBlank(source)) { return "Empty data"; } SourceEnum sourceEnum = SourceEnum.match(source); if (sourceEnum == null) { return "Empty data"; } AbstractDataService dataService = sourceEnum.dataService; if (dataService == null) { return "Empty data"; } return dataService.dataProcess(); } }
經過使用枚舉類,在枚舉中將 屬性與規則具體實現進行綁定。經過改變能夠減小if -else使得代碼更加優雅 若是須要新增渠道,咱們只須要在編寫具體規則實現類時實現DataService接口,並在枚舉類中新增的枚舉,而不須要改動到原先的任何代碼。這符合了開閉原則。
因爲Spring默認是單例模式,因此正常開發一個接口有多個實現類都是一個一個指定(@Qualifier
)裝配,可是若是想利用多態,讓程序決定由哪一個具體實現類執行,能夠將@Service
註解配置在接口上,同時爲接口屬性/方法添加@Lookup
註解。對象