摘要:注解方式優(yōu)點(diǎn)使用注解方式可以極大的減少使用模版方法模式帶來(lái)的擴(kuò)展時(shí)需要繼承模版類的弊端,工廠注解的方式可以無(wú)需關(guān)心其他業(yè)務(wù)類的實(shí)現(xiàn),而且減少了類膨脹的風(fēng)險(xiǎn)。
在上一篇文章Java設(shè)計(jì)模式綜合運(yùn)用(門面+模版方法+責(zé)任鏈+策略)中,筆者寫了一篇門面模式、模版方法、責(zé)任鏈跟策略模式的綜合運(yùn)用的事例文章,但是后來(lái)筆者發(fā)現(xiàn),在實(shí)現(xiàn)策略模式的實(shí)現(xiàn)上,發(fā)現(xiàn)了一個(gè)弊端:那就是如果在后續(xù)業(yè)務(wù)發(fā)展中,需要再次增加一個(gè)業(yè)務(wù)策略的時(shí)候,則需要再次繼承AbstractValidatorHandler類(詳情請(qǐng)參見上篇文章),這樣就會(huì)造成一定的類膨脹。今天我利用注解的方式改造成動(dòng)態(tài)策略模式,這樣就只需要關(guān)注自己的業(yè)務(wù)類即可,無(wú)需再實(shí)現(xiàn)一個(gè)類似的Handler類。1. 項(xiàng)目背景 1.1 項(xiàng)目簡(jiǎn)介
本文也同步發(fā)布至簡(jiǎn)書,地址:https://www.jianshu.com/p/b86...
在公司的一個(gè)業(yè)務(wù)系統(tǒng)中,有這樣的一個(gè)需求,就是根據(jù)不同的業(yè)務(wù)流程,可以根據(jù)不同的組合主鍵策略進(jìn)行動(dòng)態(tài)的數(shù)據(jù)業(yè)務(wù)查詢操作。在本文中,我假設(shè)有這樣兩種業(yè)務(wù),客戶信息查詢和訂單信息查詢,對(duì)應(yīng)以下枚舉類:
/** * 業(yè)務(wù)流程枚舉 * @author landyl * @create 11:18 AM 05/07/2018 */ public enum WorkflowEnum { ORDER(2), CUSTOMER(3), ; .... }
每種業(yè)務(wù)類型都有自己的組合主鍵查詢規(guī)則,并且有自己的查詢優(yōu)先級(jí),比如客戶信息查詢有以下策略:
customerId
requestId
birthDate+firstName
以上僅是假設(shè)性操作,實(shí)際業(yè)務(wù)規(guī)則比這復(fù)雜的多
1.2 流程梳理主要業(yè)務(wù)流程,可以參照以下簡(jiǎn)單的業(yè)務(wù)流程圖。
1.2.1 查詢抽象模型 1.2.2 組合主鍵查詢策略 1.2.3 組合主鍵查詢責(zé)任鏈 2. Java注解簡(jiǎn)介注解的語(yǔ)法比較簡(jiǎn)單,除了@符號(hào)的使用之外,它基本與Java固有語(yǔ)法一致。
2.1 元注解JDK1.5提供了4種標(biāo)準(zhǔn)元注解,專門負(fù)責(zé)新注解的創(chuàng)建。
注解 | 說明 |
---|---|
@Target | 表示該注解可以用于什么地方,可能的ElementType參數(shù)有: CONSTRUCTOR:構(gòu)造器的聲明 FIELD:域聲明(包括enum實(shí)例) LOCAL_VARIABLE:局部變量聲明 METHOD:方法聲明 ACKAGE:包聲明 PARAMETER:參數(shù)聲明 TYPE:類、接口(包括注解類型)或enum聲明 |
@Retention | 表示需要在什么級(jí)別保存該注解信息??蛇x的RetentionPolicy參數(shù)包括: SOURCE:注解將被編譯器丟棄 CLASS:注解在class文件中可用,但會(huì)被VM丟棄 RUNTIME:JVM將在運(yùn)行期間保留注解,因此可以通過反射機(jī)制讀取注解的信息。 |
@Document | 將注解包含在Javadoc中 |
@Inherited | 允許子類繼承父類中的注解 |
定義一個(gè)注解的方式相當(dāng)簡(jiǎn)單,如下代碼所示:
@Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented //使用@interface關(guān)鍵字定義注解 public @interface Description { /* * 注解方法的定義(其實(shí)在注解中也可以看做成員變量)有如下的規(guī)定: * 1.不能有參數(shù)和拋出異常 * 2.方法返回類型只能為八種基本數(shù)據(jù)類型和字符串,枚舉和注解以及這些類型構(gòu)成的數(shù)組 * 3.可以包含默認(rèn)值,通過default實(shí)現(xiàn) * 4.如果只有一個(gè)方法(成員變量),最好命名為value */ String value(); int count() default 1; //默認(rèn)值為1 }
注解的可用的類型包括以下幾種:所有基本類型、String、Class、enum、Annotation、以上類型的數(shù)組形式。元素不能有不確定的值,即要么有默認(rèn)值,要么在使用注解的時(shí)候提供元素的值。而且元素不能使用null作為默認(rèn)值。注解在只有一個(gè)元素且該元素的名稱是value的情況下,在使用注解的時(shí)候可以省略“value=”,直接寫需要的值即可。
2.3 使用注解如上所示的注解使用如下:
/** * @author landyl * @create 2018-01-12:39 PM */ //在類上使用定義的Description注解 @Description(value="class annotation",count=2) public class Person { private String name; private int age; //在方法上使用定義的Description注解 @Description(value="method annotation",count=3) public String speak() { return "speaking..."; } }
使用注解最主要的部分在于對(duì)注解的處理,那么就會(huì)涉及到注解處理器。從原理上講,注解處理器就是通過反射機(jī)制獲取被檢查方法上的注解信息,然后根據(jù)注解元素的值進(jìn)行特定的處理。
/** * @author landyl * @create 2018-01-12:35 PM * 注解解析類 */ public class ParseAnnotation { public static void main(String[] args){ //使用類加載器加載類 try { Class c = Class.forName("com.annatation.Person");//加載使用了定義注解的類 //找到類上的注解 boolean isExist = c.isAnnotationPresent(Description.class); if(isExist){ //拿到注解示例 Description d = (Description)c.getAnnotation(Description.class); System.out.println(d.value()); } //找到方法上的注解 Method[] ms = c.getMethods(); for(Method m : ms){ boolean isMExist = m.isAnnotationPresent(Description.class); if(isMExist){ Description d = m.getAnnotation(Description.class); System.out.println(d.value()); } } //另外一種注解方式 for(Method m:ms){ Annotation[] as = m.getAnnotations(); for(Annotation a:as){ if(a instanceof Description){ Description d = (Description)a; System.out.println(d.value()); } } } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }3. 策略模式升級(jí)版 3.1 策略模式實(shí)現(xiàn)方式
使用工廠進(jìn)行簡(jiǎn)單的封裝
使用注解動(dòng)態(tài)配置策略
使用模版方法模式配置策略(參見Java設(shè)計(jì)模式綜合運(yùn)用(門面+模版方法+責(zé)任鏈+策略))
使用工廠+注解方式動(dòng)態(tài)配置策略(利用Spring加載)
其中第1、2點(diǎn)請(qǐng)參見org.landy.strategy 包下的demo事例即可,而第4點(diǎn)的方式其實(shí)就是結(jié)合第1、2、3點(diǎn)的優(yōu)點(diǎn)進(jìn)行整合的方式。
3.2 注解方式優(yōu)點(diǎn)使用注解方式可以極大的減少使用模版方法模式帶來(lái)的擴(kuò)展時(shí)需要繼承模版類的弊端,工廠+注解的方式可以無(wú)需關(guān)心其他業(yè)務(wù)類的實(shí)現(xiàn),而且減少了類膨脹的風(fēng)險(xiǎn)。
3.3 組合主鍵查詢策略本文以組合主鍵查詢策略這一策略進(jìn)行說明,策略注解如下:
/** * 組合主鍵查詢策略(根據(jù)不同業(yè)務(wù)流程區(qū)分組合主鍵查詢策略,并且每個(gè)業(yè)務(wù)流程都有自己的優(yōu)先級(jí)策略) * @author landyl * @create 2:22 PM 09/29/2018 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface KeyIdentificationStrategy { /** * 主鍵策略優(yōu)先級(jí) * @return */ int priority() default 0; /** * 業(yè)務(wù)流程類型(如:訂單信息,會(huì)員信息等業(yè)務(wù)流程) * @return */ WorkflowEnum workflowId(); /** * the spring bean name * @return */ String beanName(); }3.4 策略工廠
既然定義了組合主鍵查詢策略注解,那必然需要一個(gè)注解處理器進(jìn)行解析注解的操作,本文以工廠的方式進(jìn)行。主要邏輯如下:
掃描指定包下的Java類,找出相應(yīng)接口(即KeyIdentification)下的所有Class對(duì)象。
private List> getIdentifications() { Set packageNames = this.getBasePackages(); List > identifications = new ArrayList<>(); if(packageNames != null) { packageNames.forEach((packageName) -> identifications.addAll(getIdentifications(packageName))); } return identifications; }
解析注解KeyIdentificationStrategy,定義一個(gè)排序?qū)ο螅?b>KeyIdentificationComparator),指定優(yōu)先級(jí)。
/** * define a comparator of the KeyIdentification object through the priority of the IdentifyPriority for sort purpose.
*/
private class KeyIdentificationComparator implements Comparator {
@Override public int compare(Object objClass1, Object objClass2) { if(objClass1 != null && objClass2 != null) { OptionalstrategyOptional1 = getPrimaryKeyIdentificationStrategy((Class)objClass1); Optional strategyOptional2 = getPrimaryKeyIdentificationStrategy((Class)objClass2); KeyIdentificationStrategy ip1 = strategyOptional1.get(); KeyIdentificationStrategy ip2 = strategyOptional2.get(); Integer priority1 = ip1.priority(); Integer priority2 = ip2.priority(); WorkflowEnum workflow1 = ip1.workflowId(); WorkflowEnum workflow2 = ip2.workflowId(); //先按業(yè)務(wù)類型排序 int result = workflow1.getValue() - workflow2.getValue(); //再按優(yōu)先級(jí)排序 if(result == 0) return priority1.compareTo(priority2); return result; } return 0; }
}
3. 根據(jù)注解,把相應(yīng)業(yè)務(wù)類型的組合主鍵查詢策略對(duì)象放入容器中(即`DefaultKeyIdentificationChain`)。
KeyIdentificationStrategy strategy = strategyOptional.get();
String beanName = strategy.beanName(); //業(yè)務(wù)流程類型 WorkflowEnum workflowId = strategy.workflowId(); KeyIdentificationStrategy priority = getPrimaryKeyIdentificationStrategy(v).get(); LOGGER.info("To add identification:{},spring bean name is:{},the identify priority is:{},workflowId:{}",simpleName,beanName,priority.priority(),workflowId.name()); KeyIdentification instance = ApplicationUtil.getApplicationContext().getBean(beanName,v);
defaultKeyIdentificationChain.addIdentification(instance,workflowId);
4. 后續(xù),在各自對(duì)應(yīng)的業(yè)務(wù)查詢組件對(duì)象中即可使用該工廠對(duì)象調(diào)用如下方法,即可進(jìn)行相應(yīng)的查詢操作。
public IdentificationResultType identify(IdentifyCriterion identifyCriterion,WorkflowEnum workflowId) {
//must set the current workflowId defaultKeyIdentificationChain.doClearIdentificationIndex(workflowId); return defaultKeyIdentificationChain.doIdentify(identifyCriterion,workflowId); }
## 4. 總結(jié) 以上就是本人在實(shí)際工作中,對(duì)第一階段使用到的設(shè)計(jì)模式的一種反思后得到的優(yōu)化結(jié)果,可能還有各種不足,但是個(gè)人感覺還是有改進(jìn),希望大家也不吝賜教,大家一起進(jìn)步才是真理。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74531.html
摘要:此案例中,門面類為,然后各個(gè)門面方法的參數(shù)均為抽象類,通過決定調(diào)用中的哪個(gè)子類。抽象類持有類的對(duì)象,并且實(shí)現(xiàn)累的一個(gè)接口是為了容器啟動(dòng)完成的時(shí)候自動(dòng)把相應(yīng)的校驗(yàn)器加入到校驗(yàn)器鏈中。 引言:很久沒有更新了,主要是工作忙。最近,工作中一個(gè)子系統(tǒng)升級(jí),把之前不易擴(kuò)展的缺點(diǎn)給改進(jìn)了一下,主要是運(yùn)用了幾個(gè)設(shè)計(jì)模式進(jìn)行稍微改造了一下。本文也同步發(fā)布至簡(jiǎn)書,地址: https://www.jians...
摘要:責(zé)任鏈模式的具體運(yùn)用以及原理請(qǐng)參見筆者責(zé)任鏈模式改進(jìn)方式引入適配器模式關(guān)于接口適配器模式原理以及使用場(chǎng)景請(qǐng)參見筆者適配器模式。 1 責(zé)任鏈模式現(xiàn)存缺點(diǎn) 由于責(zé)任鏈大多數(shù)都是不純的情況,本案例中,只要校驗(yàn)失敗就直接返回,不繼續(xù)處理接下去責(zé)任鏈中的其他校驗(yàn)邏輯了,故而出現(xiàn)如果某個(gè)部分邏輯是要由多個(gè)校驗(yàn)器組成一個(gè)整理的校驗(yàn)邏輯的話,則此責(zé)任鏈模式則顯現(xiàn)出了它的不足之處了。(責(zé)任鏈模式的具體運(yùn)...
摘要:當(dāng)然,除了讓我們顯得更加專業(yè)之外,在自己所學(xué)習(xí)或者工作的項(xiàng)目中,適當(dāng)合理的使用設(shè)計(jì)模式,能夠給項(xiàng)目帶來(lái)很大的好處。 簡(jiǎn)單說兩句 本文首發(fā)公眾號(hào)【一名打字員】 對(duì)不住各位老鐵了,年前說好要更幾波JAVA的東西,又偷懶了,沒辦法,在這里用小錘錘偷偷錘了自己幾下。由于工作原因,更新時(shí)間不定,各位老鐵有問題可以私聊我哈。 對(duì)于初學(xué)者或者是正在向中高級(jí)的Java程序猿(打字員)來(lái)說,時(shí)刻梳理自己...
摘要:抽象工廠模式是為了處理對(duì)象具有等級(jí)結(jié)構(gòu)以及對(duì)象族的問題。單例設(shè)計(jì)模式單例模式確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例,這個(gè)類成為單例類。 導(dǎo)語(yǔ):設(shè)計(jì)模式是無(wú)數(shù)碼農(nóng)前人在實(shí)際的生產(chǎn)項(xiàng)目中經(jīng)過不斷的踩坑、爬坑、修坑的經(jīng)歷總結(jié)出來(lái)的經(jīng)驗(yàn)教訓(xùn),經(jīng)過抽象之后表達(dá)成的概念。能夠幫助后來(lái)的設(shè)計(jì)者避免重復(fù)同樣的錯(cuò)誤或者彎路。我也抽空整理了一下設(shè)計(jì)模式,用自己的話總結(jié)了一下,自認(rèn)...
摘要:建議參看學(xué)習(xí)創(chuàng)建型模式工廠模式抽象工廠模式單例模式建造者模式原型模式結(jié)構(gòu)型模式適配器模式接口轉(zhuǎn)換橋接模式過濾器模式組合模式裝飾器模式外觀模式門面模式前臺(tái)接待享元模式代理模式行為型模式責(zé)任鏈模式工作流命令模式解釋器模式 建議參看github學(xué)習(xí) 1.創(chuàng)建型模式(creational) 工廠模式(factory)抽象工廠模式(abstract factory)單例模式(singleton)...
閱讀 1470·2023-04-25 17:18
閱讀 1897·2021-10-27 14:18
閱讀 2138·2021-09-09 09:33
閱讀 1854·2019-08-30 15:55
閱讀 2026·2019-08-30 15:53
閱讀 3452·2019-08-29 16:17
閱讀 3440·2019-08-26 13:57
閱讀 1741·2019-08-26 13:46