Java面試題~基于注解+Enum+策略模式優(yōu)化switch case
作者:
修羅debug
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。
摘要:本文我們將繼續(xù)分享介紹第二道關(guān)于Java面試的“代碼優(yōu)化題”,主要實(shí)現(xiàn)的功能為:基于Spring Boot采用“注解+Enum枚舉+策略模式”的思想優(yōu)化項(xiàng)目中頻繁需要增減if else的判斷或者switch case中常量的取值。
內(nèi)容:最近看到某位正在求職Java后端開(kāi)發(fā)的小伙伴留言給debug發(fā)了這么一道面試題,并咨詢相應(yīng)的解決方案:“在一個(gè)正規(guī)的企業(yè)級(jí)項(xiàng)目中,或多或少會(huì)存在著if else、if else判斷的代碼,倘若判斷的層級(jí)只是2/3層,倒還說(shuō)得過(guò)去,但倘若層級(jí)數(shù)超過(guò)了7/8層,甚至還有的超過(guò)了10層(捂臉),那對(duì)于后來(lái)的維護(hù)者而言或許會(huì)是一場(chǎng)災(zāi)難,而且,如果需要增加、刪減某一層級(jí)的判斷,那難免需要去動(dòng)那段 具有10幾層級(jí)數(shù) 的if else,如果恰巧這段代碼的改動(dòng)很有可能會(huì)影響C端、即用戶端的使用,那將是很糟糕的”
(附注:以上這種情況跟switch case代碼塊堆積體現(xiàn)出來(lái)的效果是一樣的)!
下面,直接看這段代碼吧(talk is sheap,show me the code 還是代碼比較實(shí)在,說(shuō)太多都是廢話):
public class SwitchCaseExecute {
public static CaseResponse execute(CaseRequest request){
CaseResponse response;
switch (request.getType()){
case "A":
response= methodA(request);
break;
case "B":
response= methodB(request);
break;
//………
default:
response= methodC(request);
break;
}
return response;
}
public static CaseResponse methodA(CaseRequest request){
CaseResponse response=new CaseResponse();
response.setResult("A"+request.getName());
return response;
}
public static CaseResponse methodB(CaseRequest request){
CaseResponse response=new CaseResponse();
response.setResult("B"+request.getName());
return response;
}
public static CaseResponse methodC(CaseRequest request){
CaseResponse response=new CaseResponse();
response.setResult("C"+request.getName());
return response;
}
public static void main(String[] args) {
CaseRequest request=new CaseRequest("A","這是名字");
System.out.println(execute(request));
}
}
從上面這段代碼中可以看出,如果現(xiàn)在需要增加常量值C、D、E、F….以及每個(gè)常量值對(duì)應(yīng)的“方法操作邏輯”,那么毫無(wú)疑問(wèn),需要在execute()方法中新增許許多多的case以及對(duì)應(yīng)的“方法操作邏輯”!
很顯然,這種方式雖然可以實(shí)現(xiàn)功能,但是從“面向?qū)ο笏枷搿币约啊按a簡(jiǎn)潔度”的角度來(lái)看,這顯然是有點(diǎn)冗余以及“面向過(guò)程思想”!
下面,我們從 注解+Enum+策略模式 的角度來(lái)優(yōu)化這段冗余的代碼,我們期望達(dá)到的效果是:可以只用一個(gè)注解的形式來(lái)實(shí)現(xiàn)動(dòng)態(tài)增減上面那些case對(duì)應(yīng)的常量值及其對(duì)應(yīng)的“方法操作邏輯”,其完整的過(guò)程如下所示:
(1)首先,我們需要定義一個(gè)包含上述case常量值的枚舉類CaseEnum,其源代碼如下所示:
public enum CaseEnum {
A("A"),
B("B"),
C("C"),
;
private String type;
CaseEnum(String type) {
this.type = type;
}
//省略type的getter setter方法
}
(2)緊接著,我們定義注解CaseAnnotation,這個(gè)注解將用于“方法操作邏輯實(shí)現(xiàn)類”之上,其源代碼如下所示:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CaseAnnotation {
CaseEnum value();
}
(3)然后,我們將上面那些case常量值對(duì)應(yīng)的“方法操作邏輯”進(jìn)行抽象(設(shè)計(jì)模式之一~策略模式),統(tǒng)一歸結(jié)于下面的接口中:
public interface CaseInterface {
String execute(CaseRequest request) throws Exception;
}
與此同時(shí),我們定義那些case常量值對(duì)應(yīng)的具體實(shí)現(xiàn)邏輯,即CaseInterface接口的具體實(shí)現(xiàn)類,如case常量值為A的實(shí)現(xiàn)類:
@Component
@CaseAnnotation(value = CaseEnum.A)
public class CaseAImpl implements CaseInterface{
@Override
public String execute(CaseRequest request) throws Exception {
return "結(jié)果:A -- "+request.getName();
}
}
case常量值為B的實(shí)現(xiàn)類、case常量值為C的實(shí)現(xiàn)類 在這里我就不貼出來(lái)了,大伙可以自行檢出代碼進(jìn)行查看!
(4)至此,其實(shí)我們已經(jīng)完成了一大半了,但是還有一點(diǎn),即我們?nèi)绾螌⑦@些case常量值(枚舉值)
以及 對(duì)應(yīng)的方法操作邏輯實(shí)現(xiàn)類 一一對(duì)應(yīng)起來(lái)呢!自然而然地,我們想到了采用 Map<String,CaseInterface>
進(jìn)行映射,所以我們需要在項(xiàng)目啟動(dòng)的過(guò)程中,掃描并將上面那些采用了特定的注解CaseAnnotation注解的實(shí)現(xiàn)類 的bean組件
獲取出來(lái)并添加進(jìn)Map中,即完成了 case常量值 ~ 方法操作實(shí)現(xiàn)類 的映射
因此,我們需要手動(dòng)創(chuàng)建一個(gè)用于掃描即將加入spring ioc容器的工具SpringContextUtil,其源代碼如下所示:
@Component
public class SpringContextUtil implements ApplicationContextAware{
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context=context;
}
//獲取spring應(yīng)用上下文(容器)-進(jìn)而準(zhǔn)備獲取相應(yīng)的bean
public ApplicationContext getContext(){
return context;
}
}
用于創(chuàng)建上述映射Map的Bean組件InitCaseBeanMapComponent,其完整源代碼如下所示:
@Component
public class InitCaseBeanMapComponent {
private static Map<CaseEnum,CaseInterface> processMap=new ConcurrentHashMap<>();
@Autowired
private SpringContextUtil springContextUtil;
@PostConstruct
public void init() {
//獲取那些帶上了注解的 處理實(shí)現(xiàn)類
Map<String,Object> map=springContextUtil.getContext().getBeansWithAnnotation(CaseAnnotation.class);
System.out.println("此時(shí)獲取出來(lái)的是:"+map);
//添加 case常量值~方法操作邏輯實(shí)現(xiàn)類 映射
for (Object process:map.values()){
CaseAnnotation annotation=process.getClass().getAnnotation(CaseAnnotation.class);
processMap.put(annotation.value(),(CaseInterface) process);
}
}
public Map<CaseEnum,CaseInterface> getProcessMap(){
return processMap;
}
}
(5)最后當(dāng)然是進(jìn)行收尾了,在BaseController開(kāi)發(fā)一方法,用于接收“調(diào)用端-如前端、客戶端等等”發(fā)起請(qǐng)求某個(gè)常量值A(chǔ)、B、C、D…等等時(shí),看看得到的相應(yīng)結(jié)果:
@Autowired
private InitCaseBeanMapComponent mapComponent;
@RequestMapping(value = "/switch/case",method = RequestMethod.GET)
public BaseResponse caseInfo(@RequestParam String type,@RequestParam String name){
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
CaseRequest request=new CaseRequest(type,name);
CaseEnum caseEnum=CaseEnum.valueOf(request.getType());
CaseInterface caseInterface= mapComponent.getProcessMap().get(caseEnum);
response.setData(caseInterface.execute(request));
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
最后當(dāng)然是發(fā)起相應(yīng)的請(qǐng)求,請(qǐng)求A、B、D并觀察返回的結(jié)果,然后觀察相應(yīng)的響應(yīng)結(jié)果,可以看到結(jié)果當(dāng)然是正確的:
此時(shí),如果想要新增一個(gè)case常量值以及對(duì)應(yīng)的方法處理邏輯,則不需要?jiǎng)覤aseController的代碼(面向用戶發(fā)起的請(qǐng)求代碼),而只需要添加一個(gè)實(shí)現(xiàn)類以及一個(gè)注解,并將該注解加到該實(shí)現(xiàn)類上面,如下圖所以:
當(dāng)然啦,從上面改造的整個(gè)過(guò)程來(lái)看,會(huì)發(fā)現(xiàn)一個(gè)蛋疼的事實(shí),“優(yōu)化后的代碼比原先的代碼更多了,哈哈”!這一點(diǎn)確實(shí)是,但是人呢,眼光還是放長(zhǎng)遠(yuǎn)一點(diǎn),當(dāng)層級(jí)數(shù)多的時(shí)候,你會(huì)發(fā)現(xiàn)Controller的代碼或者Service的代碼會(huì)過(guò)千行、過(guò)萬(wàn)行。。。當(dāng)然啦,在這一整體的改造過(guò)程中,也從另外一個(gè)角度證明了一件事情:干大事從來(lái)都得犧牲點(diǎn)什么!
補(bǔ)充:
1、本文涉及到的相關(guān)的源代碼可以到此地址,check出來(lái)進(jìn)行查看學(xué)習(xí):
https://gitee.com/steadyjack/SpringBootTechnology
2、關(guān)注一下Debug的技術(shù)微信公眾號(hào),最新的技術(shù)文章、課程以及技術(shù)專欄將會(huì)第一時(shí)間在公眾號(hào)發(fā)布哦!