@order注解_别再用ifelse了,用注解去代替他吧
策略模式
經(jīng)常在網(wǎng)上看到一些名為“別再if-else走天下了”,“教你干掉if-else”等之類(lèi)的文章,大部分都會(huì)講到用策略模式去代替if-else。策略模式實(shí)現(xiàn)的方式也大同小異。主要是定義統(tǒng)一行為(接口或抽象類(lèi)),并實(shí)現(xiàn)不同策略下的處理邏輯(對(duì)應(yīng)實(shí)現(xiàn)類(lèi))。客戶(hù)端使用時(shí)自己選擇相應(yīng)的處理類(lèi),利用工廠(chǎng)或其他方式。
注解實(shí)現(xiàn)
本文要說(shuō)的是用注解實(shí)現(xiàn)策略模式的方式,以及一些注意點(diǎn)。
話(huà)不多說(shuō),還是以最常 見(jiàn)的訂單處理為例。首先定義這樣一個(gè)訂單實(shí)體類(lèi):
假如對(duì)于不同來(lái)源(pc端、移動(dòng)端)的訂單需要不同的邏輯處理。項(xiàng)目中一般會(huì)有OrderService這樣一個(gè)類(lèi),如下,里面有一坨if-else的邏輯,目的是根據(jù)訂單的來(lái)源的做不同的處理。
@Service策略模式就是要干掉上面的一坨if-else,使得代碼看起來(lái)優(yōu)雅且高大上。
現(xiàn)在就讓我們開(kāi)始干掉這一坨if-else。先總覽下結(jié)構(gòu):
1.首先定義一個(gè)OrderHandler接口,此接口規(guī)定了處理訂單的方法。public?
2.定義一個(gè)OrderHandlerType注解,來(lái)表示某個(gè)類(lèi)是用來(lái)處理何種來(lái)源的訂單。
@Target(ElementType.TYPE)3.接下來(lái)就是實(shí)現(xiàn)pc端和移動(dòng)端訂單處理各自的handler,并加上我們所定義的OrderHandlerType注解。
@OrderHandlerType(source?=?4.以上準(zhǔn)備就緒后,就是向spring容器中注入各種訂單處理的handler,并在OrderService.orderService方法中,通過(guò)策略(訂單來(lái)源)去決定選擇哪一個(gè)OrderHandler去處理訂單。我們可以這樣做:
@Service在OrderService中,維護(hù)了一個(gè)orderHandleMap,它的key為訂單來(lái)源,value為對(duì)應(yīng)的訂單處理器Handler。通過(guò)@Autowired去初始化orderHandleMap(這里有一個(gè)lambda表達(dá)式,仔細(xì)看下其實(shí)沒(méi)什么難度的)。這樣一來(lái),OrderService.orderService里的一坨if-else不見(jiàn)了,取而代之的僅僅是兩行代碼。即,先從orderHandleMap中根據(jù)訂單來(lái)源獲取對(duì)應(yīng)的OrderHandler,然后執(zhí)行OrderHandler.handle方法即可。
這種做法的好處是,不論以后業(yè)務(wù)如何發(fā)展致使訂單來(lái)源種類(lèi)增加,OrderService的核心邏輯不會(huì)改變,我們只需要實(shí)現(xiàn)新增來(lái)源的OrderHandler即可,且團(tuán)隊(duì)中每人開(kāi)發(fā)各自負(fù)責(zé)的訂單來(lái)源對(duì)應(yīng)的OrderHandler即可,彼此間互不干擾。
到此,似乎已經(jīng)講完了通過(guò)注解實(shí)現(xiàn)策略模式,干掉if-else的方法,就這樣結(jié)束了嗎?不,真正的重點(diǎn)從現(xiàn)在才開(kāi)始。
現(xiàn)在回過(guò)頭看orderHandleMap這個(gè)Map,它的key是訂單來(lái)源,假如,我們想通過(guò)訂單來(lái)源+訂單支付方式這兩個(gè)屬性來(lái)決定到底使用哪一種OrderHandler怎么辦?比如PC端支付寶支付的訂單是一種處理邏輯(PCAliPayOrderHandler),PC端微信支付的訂單是另外一種處理邏輯(PCWeChatOrderHandler),對(duì)應(yīng)的還有移動(dòng)端支付寶支付(MobileAliPayOrderHandler)和移動(dòng)端微信支付(MobileWeChatOrderHandler)。
這時(shí)候我們的key應(yīng)該存什么呢,可能有人會(huì)說(shuō),我直接存訂單來(lái)源+訂單支付方式組成的字符串不就行了嗎?確實(shí)可以,但是如果這時(shí)業(yè)務(wù)邏輯又變了(向pm低頭),PC端支付寶支付和微信支付是同一種處理邏輯,而移動(dòng)端支付寶支付和微信支付是不同的處理邏輯,那情況就變成了PCAliPayOrderHandler和PCWeChatOrderHandler這兩個(gè)類(lèi)是同一套代碼邏輯。我們干掉了if-else,但卻造出了兩份相同的代碼,這是一個(gè)作為有強(qiáng)迫癥的程序員所不能容忍的。怎么干掉這兩個(gè)邏輯相同的類(lèi)呢?
首先,我們可以回顧下,注解它究竟是個(gè)什么玩意?不知道大家有沒(méi)有注意到定義注解的語(yǔ)法,也就是@interface,與定義接口的語(yǔ)法想比,僅僅多了一個(gè)@。翻看jdk,可以找到這么一個(gè)接口Annotation,如下
/**?*?The?common?interface?extended?by?all?annotation?types.??Note?that?an
?*?interface?that?manually?extends?this?one?does?not?define
?*?an?annotation?type.??Also?note?that?this?interface?does?not?itself
?*?define?an?annotation?type.
?*
?*?More?information?about?annotation?types?can?be?found?in?section?9.6?of
?*?The?Java??Language?Specification.
?*
?*?The?{@link?java.lang.reflect.AnnotatedElement}?interface?discusses
?*?compatibility?concerns?when?evolving?an?annotation?type?from?being
?*?non-repeatable?to?being?repeatable.
?*
?*?@author??Josh?Bloch
?*?@since???1.5
?*/
開(kāi)頭就表明了,The common interface extended by all annotation types。說(shuō)的很明白了,其實(shí)注解它就是個(gè)接口,對(duì),它就是個(gè)接口而已,@interface僅僅是個(gè)語(yǔ)法糖。那么,注解既然是個(gè)接口,就必然會(huì)有相應(yīng)的實(shí)現(xiàn)類(lèi),那實(shí)現(xiàn)類(lèi)哪里來(lái)呢?上述中我們僅僅定義了OrderHandlerType注解,別的什么也沒(méi)有做。這時(shí)候不得不提動(dòng)態(tài)代理了,一定是jdk在背后為我們做了些什么。
為了追蹤JVM在運(yùn)行過(guò)程中生成的JDK動(dòng)態(tài)代理類(lèi)。我們可以設(shè)置VM啟動(dòng)參數(shù)如下:
該參數(shù)可以保存所生成的JDK動(dòng)態(tài)代理類(lèi)到本地。額外說(shuō)一句,若我們想追蹤cglib所生成的代理類(lèi),即對(duì)應(yīng)的字節(jié)碼文件,可以設(shè)置參數(shù):
"保存的路徑");添加參數(shù)后再次啟動(dòng)項(xiàng)目,可以看到我們項(xiàng)目里多了許多class文件(這里只截取了部分,筆者啟動(dòng)的項(xiàng)目共生成了97個(gè)動(dòng)態(tài)代理類(lèi)。由于我的項(xiàng)目是springboot環(huán)境,因此生成了許多如ConditionalOnMissingBean、Configuration、Autowired等注解對(duì)應(yīng)的代理類(lèi)):
那接下來(lái)就是要找到我們所關(guān)心的,即jdk為我們自定義的OrderHandlerType注解所生成的代理類(lèi)。由于jdk生成的動(dòng)態(tài)代理類(lèi)都會(huì)繼承Proxy這個(gè)類(lèi),而java又是單繼承的,所以,我們只需要找到實(shí)現(xiàn)了OrderHandlerType的類(lèi)即可。遍歷這些類(lèi)文件,發(fā)現(xiàn)$Proxy63.class實(shí)現(xiàn)了我們定義的OrderHandlerType注解:
public?我們知道,jdk動(dòng)態(tài)代理其實(shí)現(xiàn)的核心是:
也就是這個(gè)構(gòu)造函數(shù)的InvocationHandler對(duì)象了。那么注解的InvocationHandler是哪個(gè)具體實(shí)現(xiàn)呢?不難發(fā)現(xiàn)就是:
這個(gè)類(lèi)里面的核心屬性,就是那個(gè) memberValues,我們?cè)谑褂米⒔鈺r(shí)給注解屬性的賦值,都存儲(chǔ)在這個(gè)map里了。而代理類(lèi)中的各種方法的實(shí)現(xiàn),實(shí)際上是調(diào)用了 AnnotationInvocationHandler 里的 invoke 方法。
好了,現(xiàn)在我們知道了注解就是個(gè)接口,且通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)其中所定義的各種方法。那么回到我們的OrderService,為什么不把key的類(lèi)型設(shè)置為OrderHandlerType?就像這樣。private?Map
如此一來(lái),不管決定訂單處理器orderhandler的因素怎么變,我們便可以以不變應(yīng)萬(wàn)變(這不就是我們所追求的代碼高擴(kuò)展性和靈活性么)。那當(dāng)我們的map的key變成了OrderHandlerType之后,注入和獲取的邏輯就要相應(yīng)改變,注入的地方很好改變,如下:
public?那獲取的邏輯要怎么實(shí)現(xiàn)?我們?cè)趺锤鶕?jù)order的來(lái)源和支付方式去orderHandleMap里獲取對(duì)應(yīng)的OrderHandler呢?問(wèn)題變成了如何關(guān)聯(lián)order的來(lái)源和支付方式與OrderHandlerType注解。
還記得剛才所說(shuō)的注解就是個(gè)接口嗎,既然是個(gè)接口,我們自己實(shí)現(xiàn)一個(gè)類(lèi)不就完事了么,這樣就把order的來(lái)源和支付方式與OrderHandlerType注解關(guān)聯(lián)起來(lái)了。說(shuō)干就干,現(xiàn)在我們有了這么一個(gè)類(lèi),
在獲取對(duì)應(yīng)OrderHandler時(shí)我們可以這樣寫(xiě),
public?void?orderService(Order?order)?{看起來(lái)沒(méi)什么問(wèn)題了,來(lái)運(yùn)行一下。不對(duì)勁啊,空指針,那個(gè)異常它來(lái)了。
我們斷點(diǎn)打在NPE那一行,
一定是姿勢(shì)不對(duì),漏掉了什么。那我們就來(lái)分析下。orderHandleMap中確實(shí)注入了所定義的幾個(gè)OrderHandler(PCAliPayOrderHandler、PCWeChatOrderHandler、MobileAliPayOrderHandler、MobileWeChatOrderHandler),但是get卻沒(méi)有獲取到,這是為什么呢?我們來(lái)回憶下,map的get方法邏輯,還記得最開(kāi)始學(xué)習(xí)java時(shí)的hashCode和equals方法嗎?
不知道大家注意到?jīng)]有,map中key對(duì)應(yīng)的類(lèi)型是$Proxy63(jdk動(dòng)態(tài)代理給我們生成的),跟我們自己實(shí)現(xiàn)的OrderHandlerTypeImpl是不同類(lèi)型的。
梳理下,在Autowied時(shí),我們放進(jìn)orderHandleMap的key是動(dòng)態(tài)代理類(lèi)對(duì)象,而獲取時(shí),自定義了OrderHandlerTypeI實(shí)現(xiàn)類(lèi)OrderHandlerTypeImpl,而又沒(méi)有重寫(xiě)hashCode和equals方法,才導(dǎo)致從map中沒(méi)有獲取到我們所想要的OrderHandler,那么,我們把實(shí)現(xiàn)類(lèi)OrderHandlerTypeImpl的hashCode和equals這兩個(gè)方法重寫(xiě),保持跟動(dòng)態(tài)代理類(lèi)生成的一樣不就行了嗎?再回看下動(dòng)態(tài)代理給我們生成的這兩個(gè)方法,前面說(shuō)了,注解對(duì)應(yīng)的代理類(lèi)方法調(diào)用實(shí)際上都是AnnotationInvocationHandler里面的方法,翻看AnnotationInvocationHandler里面的hashCode和equals方法:private?int?hashCodeImpl()?{
具體的邏輯也比較簡(jiǎn)單,就不分析了。那我們就按照AnnotationInvocationHandler中的實(shí)現(xiàn),在我們的OrderHandlerTypeImpl中按照相同的邏輯重寫(xiě)下這兩個(gè)方法,如下
public?再次運(yùn)行看看是否達(dá)到我們預(yù)期,果不其然,這次可以正常獲取到了handler,至此,大功告成。
這樣以來(lái),不管以后業(yè)務(wù)怎么發(fā)展,OrderService核心邏輯不會(huì)改變,只需要擴(kuò)展OrderHandler即可。
如果大家覺(jué)得這篇文章對(duì)你有幫助,你的關(guān)注和轉(zhuǎn)發(fā)是對(duì)我最大的支持,O(∩_∩)O:
●編號(hào)1299,輸入編號(hào)直達(dá)本文
●輸入m獲取文章目錄
程序員求職面試分享程序員找工作經(jīng)驗(yàn)
程序員筆試、面試題
總結(jié)
以上是生活随笔為你收集整理的@order注解_别再用ifelse了,用注解去代替他吧的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mathwin.top 联系我们_设计:
- 下一篇: eks volumn s3_威客电竞 深