摘要:注解方式優點使用注解方式可以極大的減少使用模版方法模式帶來的擴展時需要繼承模版類的弊端,工廠注解的方式可以無需關心其他業務類的實現,而且減少了類膨脹的風險。
在上一篇文章Java設計模式綜合運用(門面+模版方法+責任鏈+策略)中,筆者寫了一篇門面模式、模版方法、責任鏈跟策略模式的綜合運用的事例文章,但是后來筆者發現,在實現策略模式的實現上,發現了一個弊端:那就是如果在后續業務發展中,需要再次增加一個業務策略的時候,則需要再次繼承AbstractValidatorHandler類(詳情請參見上篇文章),這樣就會造成一定的類膨脹。今天我利用注解的方式改造成動態策略模式,這樣就只需要關注自己的業務類即可,無需再實現一個類似的Handler類。1. 項目背景 1.1 項目簡介
本文也同步發布至簡書,地址:https://www.jianshu.com/p/b86...
在公司的一個業務系統中,有這樣的一個需求,就是根據不同的業務流程,可以根據不同的組合主鍵策略進行動態的數據業務查詢操作。在本文中,我假設有這樣兩種業務,客戶信息查詢和訂單信息查詢,對應以下枚舉類:
/** * 業務流程枚舉 * @author landyl * @create 11:18 AM 05/07/2018 */ public enum WorkflowEnum { ORDER(2), CUSTOMER(3), ; .... }
每種業務類型都有自己的組合主鍵查詢規則,并且有自己的查詢優先級,比如客戶信息查詢有以下策略:
customerId
requestId
birthDate+firstName
以上僅是假設性操作,實際業務規則比這復雜的多
1.2 流程梳理主要業務流程,可以參照以下簡單的業務流程圖。
1.2.1 查詢抽象模型 1.2.2 組合主鍵查詢策略 1.2.3 組合主鍵查詢責任鏈 2. Java注解簡介注解的語法比較簡單,除了@符號的使用之外,它基本與Java固有語法一致。
2.1 元注解JDK1.5提供了4種標準元注解,專門負責新注解的創建。
注解 | 說明 |
---|---|
@Target | 表示該注解可以用于什么地方,可能的ElementType參數有: CONSTRUCTOR:構造器的聲明 FIELD:域聲明(包括enum實例) LOCAL_VARIABLE:局部變量聲明 METHOD:方法聲明 ACKAGE:包聲明 PARAMETER:參數聲明 TYPE:類、接口(包括注解類型)或enum聲明 |
@Retention | 表示需要在什么級別保存該注解信息。可選的RetentionPolicy參數包括: SOURCE:注解將被編譯器丟棄 CLASS:注解在class文件中可用,但會被VM丟棄 RUNTIME:JVM將在運行期間保留注解,因此可以通過反射機制讀取注解的信息。 |
@Document | 將注解包含在Javadoc中 |
@Inherited | 允許子類繼承父類中的注解 |
定義一個注解的方式相當簡單,如下代碼所示:
@Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented //使用@interface關鍵字定義注解 public @interface Description { /* * 注解方法的定義(其實在注解中也可以看做成員變量)有如下的規定: * 1.不能有參數和拋出異常 * 2.方法返回類型只能為八種基本數據類型和字符串,枚舉和注解以及這些類型構成的數組 * 3.可以包含默認值,通過default實現 * 4.如果只有一個方法(成員變量),最好命名為value */ String value(); int count() default 1; //默認值為1 }
注解的可用的類型包括以下幾種:所有基本類型、String、Class、enum、Annotation、以上類型的數組形式。元素不能有不確定的值,即要么有默認值,要么在使用注解的時候提供元素的值。而且元素不能使用null作為默認值。注解在只有一個元素且該元素的名稱是value的情況下,在使用注解的時候可以省略“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..."; } }
使用注解最主要的部分在于對注解的處理,那么就會涉及到注解處理器。從原理上講,注解處理器就是通過反射機制獲取被檢查方法上的注解信息,然后根據注解元素的值進行特定的處理。
/** * @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. 策略模式升級版 3.1 策略模式實現方式
使用工廠進行簡單的封裝
使用注解動態配置策略
使用模版方法模式配置策略(參見Java設計模式綜合運用(門面+模版方法+責任鏈+策略))
使用工廠+注解方式動態配置策略(利用Spring加載)
其中第1、2點請參見org.landy.strategy 包下的demo事例即可,而第4點的方式其實就是結合第1、2、3點的優點進行整合的方式。
3.2 注解方式優點使用注解方式可以極大的減少使用模版方法模式帶來的擴展時需要繼承模版類的弊端,工廠+注解的方式可以無需關心其他業務類的實現,而且減少了類膨脹的風險。
3.3 組合主鍵查詢策略本文以組合主鍵查詢策略這一策略進行說明,策略注解如下:
/** * 組合主鍵查詢策略(根據不同業務流程區分組合主鍵查詢策略,并且每個業務流程都有自己的優先級策略) * @author landyl * @create 2:22 PM 09/29/2018 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface KeyIdentificationStrategy { /** * 主鍵策略優先級 * @return */ int priority() default 0; /** * 業務流程類型(如:訂單信息,會員信息等業務流程) * @return */ WorkflowEnum workflowId(); /** * the spring bean name * @return */ String beanName(); }3.4 策略工廠
既然定義了組合主鍵查詢策略注解,那必然需要一個注解處理器進行解析注解的操作,本文以工廠的方式進行。主要邏輯如下:
掃描指定包下的Java類,找出相應接口(即KeyIdentification)下的所有Class對象。
private List> getIdentifications() { Set packageNames = this.getBasePackages(); List > identifications = new ArrayList<>(); if(packageNames != null) { packageNames.forEach((packageName) -> identifications.addAll(getIdentifications(packageName))); } return identifications; }
解析注解KeyIdentificationStrategy,定義一個排序對象(KeyIdentificationComparator),指定優先級。
/** * 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(); //先按業務類型排序 int result = workflow1.getValue() - workflow2.getValue(); //再按優先級排序 if(result == 0) return priority1.compareTo(priority2); return result; } return 0; }
}
3. 根據注解,把相應業務類型的組合主鍵查詢策略對象放入容器中(即`DefaultKeyIdentificationChain`)。
KeyIdentificationStrategy strategy = strategyOptional.get();
String beanName = strategy.beanName(); //業務流程類型 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. 后續,在各自對應的業務查詢組件對象中即可使用該工廠對象調用如下方法,即可進行相應的查詢操作。
public IdentificationResultType identify(IdentifyCriterion identifyCriterion,WorkflowEnum workflowId) {
//must set the current workflowId defaultKeyIdentificationChain.doClearIdentificationIndex(workflowId); return defaultKeyIdentificationChain.doIdentify(identifyCriterion,workflowId); }
## 4. 總結 以上就是本人在實際工作中,對第一階段使用到的設計模式的一種反思后得到的優化結果,可能還有各種不足,但是個人感覺還是有改進,希望大家也不吝賜教,大家一起進步才是真理。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74531.html
摘要:此案例中,門面類為,然后各個門面方法的參數均為抽象類,通過決定調用中的哪個子類。抽象類持有類的對象,并且實現累的一個接口是為了容器啟動完成的時候自動把相應的校驗器加入到校驗器鏈中。 引言:很久沒有更新了,主要是工作忙。最近,工作中一個子系統升級,把之前不易擴展的缺點給改進了一下,主要是運用了幾個設計模式進行稍微改造了一下。本文也同步發布至簡書,地址: https://www.jians...
摘要:責任鏈模式的具體運用以及原理請參見筆者責任鏈模式改進方式引入適配器模式關于接口適配器模式原理以及使用場景請參見筆者適配器模式。 1 責任鏈模式現存缺點 由于責任鏈大多數都是不純的情況,本案例中,只要校驗失敗就直接返回,不繼續處理接下去責任鏈中的其他校驗邏輯了,故而出現如果某個部分邏輯是要由多個校驗器組成一個整理的校驗邏輯的話,則此責任鏈模式則顯現出了它的不足之處了。(責任鏈模式的具體運...
摘要:當然,除了讓我們顯得更加專業之外,在自己所學習或者工作的項目中,適當合理的使用設計模式,能夠給項目帶來很大的好處。 簡單說兩句 本文首發公眾號【一名打字員】 對不住各位老鐵了,年前說好要更幾波JAVA的東西,又偷懶了,沒辦法,在這里用小錘錘偷偷錘了自己幾下。由于工作原因,更新時間不定,各位老鐵有問題可以私聊我哈。 對于初學者或者是正在向中高級的Java程序猿(打字員)來說,時刻梳理自己...
摘要:抽象工廠模式是為了處理對象具有等級結構以及對象族的問題。單例設計模式單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例,這個類成為單例類。 導語:設計模式是無數碼農前人在實際的生產項目中經過不斷的踩坑、爬坑、修坑的經歷總結出來的經驗教訓,經過抽象之后表達成的概念。能夠幫助后來的設計者避免重復同樣的錯誤或者彎路。我也抽空整理了一下設計模式,用自己的話總結了一下,自認...
閱讀 1442·2023-04-25 17:18
閱讀 1882·2021-10-27 14:18
閱讀 2123·2021-09-09 09:33
閱讀 1839·2019-08-30 15:55
閱讀 2016·2019-08-30 15:53
閱讀 3439·2019-08-29 16:17
閱讀 3429·2019-08-26 13:57
閱讀 1730·2019-08-26 13:46