版權聲明:本文為博主原創文章,轉載請注明出處,歡迎使勁噴
雖然推崇在java中使用枚舉(可查看《Java中的枚舉的治理》)來對數據字典及常量進行控制,但是有些時候,我們還是會覺得常量控制更為便捷。
比如,對於數據字典,我們可以使用枚舉值來處理;對於一些其他的信息,我們會使用常量保存和使用。
一、常量遇到的問題
1.苗條的常量類
這里使用苗條形容下我們程序中的常量類,別看它寬度,就只看她長度,滾起屏來,那叫一個長啊,修長的身材,令你如痴如醉。(省略號里的東西,我就不貼了!!!)
例如:
public class Constants { public static final String REAL_NAME1 = "v1"; public static final String REAL_NAME2 = "v2"; public static final String REAL_NAME3 = "v3"; public static final String REAL_NAME4 = "v4"; public static final String REAL_NAME5 = "v5"; public static final String 6 = "v6"; public static final String 7 = "v7"; public static final String REAL_NAME8 = "v8"; public static final String REAL_NAME9 = "v9"; ...... }
一個無窮盡的常量類,設想這個類篇幅巨長,你想加點什么不知道該加在哪;你想改點什么,不知道去哪改;你想罵街也不知道去哪罵!!!
這樣的常量管理,帶來的問題如下:
a.不好維護,相關的代碼寫到一起,此時,常量的篇幅較長導致你找不到對應的常量塊進行維護;
b.雖然是在不同的業務場景下,但是有些常量的名稱還是有可能重復;
c.有時為了減少常量的定義,就得共用一些常量,而這樣的共用會導致某種業務場景下需要對該常量進行修改,而導致另外一些業務場景下的常量使用產生歧義;
d.其他你能想到的罵街的理由
2.代碼的壞味道
引用下“代碼的壞味道”這個詞,我們常能看到一些常量類的壞味道里,假如常量名稱如上所示,名稱類似的很多;名稱不明確的也很多,還沒有注釋,這樣的歧義也是因為代碼不好管理造成的。
二、常量如何治理
1.初級治理——使用內部類
使用java的內部類進行常量的初步治理,代碼如下:
/** * Created by SZBright on 2017/3/1. * * @author : */ public class Constants { public static final class TOKEN_FLAG_ONE { public static final String REAL_NAME = "v1"; public static final String CRET = "v2"; public static final String GUR = "v5"; } }
這樣的好處是,通過常量的內部類的名稱,可以直接獲取對應模塊的常量的引用信息。使用代碼如下:
@Test public void test(){ System.out.println(Constants.TOKEN_FLAG_ONE.REAL_NAME); }
2.中極治理——集中管理
初級治理中,我們的想法還是不錯的,但是看起來比較low,而且當我們希望通過value獲取到key時,你卻無能為力,於是我們有了中級治理。
中級治理我們主要是通過map,每個內部類都會存為map中的一個entry,每個entry又都是map類型的集合,集合中包含該內部類的所有常量。
代碼如下:
/** * Created by SZBright on 2017/3/1. * * @author : */ public class Constants { public static final Map<String,Map<String,String>> keyValueMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> valueKeyMapCons = new LinkedHashMap<String, Map<String, String>>(); /** * 初始化所有常量 */ static { try { //獲取所有內部類 for (Class cls : Constants.class.getClasses()) { Map<String, String> keyValueMap = new LinkedHashMap<String, String>();//存放key和value的map Map<String, String> valueKeyMap = new LinkedHashMap<String, String>();//存放value和key的map//每個內部類-獲取所有屬性(不包括父類的) for (Field fd : cls.getDeclaredFields()) { keyValueMap.put(fd.getName(), fd.get(cls).toString());//注解對象空,其值為該field的值 valueKeyMap.put(fd.get(cls).toString(),fd.getName()); } keyValueMapCons.put(cls.getSimpleName(),keyValueMap); valueKeyMapCons.put(cls.getSimpleName(),valueKeyMap); } } catch (Exception e) { e.printStackTrace(); } }
public static final class TOKEN_FLAG_ONE { public static final String REAL_NAME = "v1"; public static final String CRET = "v2"; public static final String GUR = "v5"; } }
好了,我們在Constants中有了兩個常量集:一個集keyValueMapCons,按照內部類的名稱,把常量的名字作為map的key,值作為map的value;一個集valueKeyMapCons,按照內部類的名稱,把常量的值作為map的key,常量的名字作為map的key。
這樣一來,我們可以使用常量的這兩個集滿足我們對常量的使用需求。使用代碼如下:
@Test public void test1(){ System.out.println(Constants.keyValueMapCons.get("TOKEN_FLAG_ONE").get("REAL_NAME"));//v1 System.out.println(Constants.valueKeyMapCons.get("TOKEN_FLAG_ONE").get("v5"));//GUR }
3.中高級治理——使用注解
我們可以通過key獲取到value,也可以通過value獲取到key了。現在有這么個問題,我們使用常量時,不光要有常量的定義、常量的值,還應該有對常量的描述,而傳統的對於常量的定義,往往使我們無從存放對常量的描述。
此時,我們希望通過注解來改變這種情況。
我們來自定義注解類型如下:
/** * Created by SZBright on 2017/3/1. * * @author : */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE}) public @interface ConstantAnnotation { String value(); }
有了這個注解,我們就可以把描述些到常量的上面了
/** * Created by SZBright on 2017/3/1. * * @author : */ public class Constants { private static final String CONSTANT_STRING = "這是一條短信消息"; public static final class TOKEN_FLAG_ONE { @ConstantAnnotation("實名") public static final String REAL_NAME = "v1"; @ConstantAnnotation("證書") public static final String CRET = "v2"; @ConstantAnnotation(CONSTANT_STRING) public static final String GUR = "v5";//把描述與注解分開 } }
我們看到,每個常量上面有了對應的中文描述,這樣的中文描述可以用來干嘛呢?
比如,我們希望在某個業務場景下,符合gur常量的業務,發送一條短信消息,而這個消息我們就可以定義在我們的自定義注解中。例如GUR這個常量,我們把它的描述聲明成一個常量,這個常量可用來存放對應的短信消息。我們的常量類中如果再有一個通過常量獲取到描述的map,這是不是就完美了?
於是,我們有了下面的代碼:
/** * Created by SZBright on 2017/3/1. * * @author : */ public class Constants { public static final Map<String,Map<String,String>> keyDescMapCons = new LinkedHashMap<String, Map<String, String>>(); /** * 初始化所有常量 */ static { try { //獲取所有內部類 for (Class cls : Constants.class.getClasses()) { Map<String, String> keyDescMap = new LinkedHashMap<String, String>();//存放key和desc的map //每個內部類-獲取所有屬性(不包括父類的) for (Field fd : cls.getDeclaredFields()) { //每個屬性獲取指定的annotation的注解對象 ConstantAnnotation ca = fd.getAnnotation(ConstantAnnotation.class); if(ca != null){ keyDescMap.put(fd.getName(), ca.value());//注解對象不空,其值為注解對象中的值 } } keyDescMapCons.put(cls.getSimpleName(),keyDescMap); } } catch (Exception e) { e.printStackTrace(); } } /** * key-value-desc * * 常量名-常量值-注解描述 * * 實現通過key獲取value * * 實現通過key獲取desc * * 實現通過value獲取desc */ private static final String CONSTANT_STRING = "這是一條短消息"; public static final class TOKEN_FLAG_ONE { @ConstantAnnotation("實名") public static final String REAL_NAME = "v1"; @ConstantAnnotation("證書") public static final String CRET = "v2"; @ConstantAnnotation(CONSTANT_STRING) public static final String GUR = "v5";//把描述與注解分開 } }
現在,我們通過Constants的keyDescMap可以獲取到這個常量對應的描述了。使用代碼如下:
@Test public void test1(){ System.out.println(Constants.keyDescMapCons.get("TOKEN_FLAG_ONE").get("GUR"));//打印輸出“這是一條短消息” }
4.綜合治理(終極治理)
現在我們有了常量的治理,有了注解的描述,有時我們需要通過key獲取到value,有時我們需要通過value獲取描述,有時我們需要通過key獲取到描述,等等。排列組合共6種形式,暫且不說什么時候會用到,我們先給他來個綜合治理的實現。代碼如下:
/** * Created by SZBright on 2017/3/1. * * @author : */ public class Constants { public static final Map<String,Map<String,String>> keyValueMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> keyDescMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> descValueMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> descKeyMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> valueDescMapCons = new LinkedHashMap<String, Map<String, String>>(); public static final Map<String,Map<String,String>> valueKeyMapCons = new LinkedHashMap<String, Map<String, String>>(); /** * 初始化所有常量 */ static { try { //獲取所有內部類 for (Class cls : Constants.class.getClasses()) { Map<String, String> keyDescMap = new LinkedHashMap<String, String>();//存放key和desc的map Map<String, String> keyValueMap = new LinkedHashMap<String, String>();//存放key和value的map Map<String, String> valueKeyMap = new LinkedHashMap<String, String>();//存放value和key的map Map<String, String> valueDescMap = new LinkedHashMap<String, String>();//存放value和desc的map Map<String, String> descValueMap = new LinkedHashMap<String, String>();//存放desc和value的map Map<String, String> descKeyMap = new LinkedHashMap<String, String>();//存放desc和key的map //每個內部類-獲取所有屬性(不包括父類的) for (Field fd : cls.getDeclaredFields()) { //每個屬性獲取指定的annotation的注解對象 ConstantAnnotation ca = fd.getAnnotation(ConstantAnnotation.class); keyValueMap.put(fd.getName(), fd.get(cls).toString());//注解對象空,其值為該field的值 valueKeyMap.put(fd.get(cls).toString(),fd.getName()); if(ca != null){ keyDescMap.put(fd.getName(), ca.value());//注解對象不空,其值為注解對象中的值 valueDescMap.put(fd.get(cls).toString(),ca.value()); descValueMap.put(ca.value(),fd.get(cls).toString()); descKeyMap.put(ca.value(),fd.getName()); } } keyValueMapCons.put(cls.getSimpleName(),keyValueMap); keyDescMapCons.put(cls.getSimpleName(),keyDescMap); descValueMapCons.put(cls.getSimpleName(),descValueMap); descKeyMapCons.put(cls.getSimpleName(),descKeyMap); valueDescMapCons.put(cls.getSimpleName(),valueDescMap); valueKeyMapCons.put(cls.getSimpleName(),valueKeyMap); } } catch (Exception e) { e.printStackTrace(); } } private static final String CONSTANT_STRING = "這是一條短消息"; public static final class TOKEN_FLAG_ONE { @ConstantAnnotation("實名") public static final String REAL_NAME = "v1"; @ConstantAnnotation("證書") public static final String CRET = "v2"; @ConstantAnnotation(CONSTANT_STRING) public static final String GUR = "v5";//把描述與注解分開 } }
好了,有了上面這個類,你就啥都不用愁了,用的時候先拿常量的聲明,再用對應的map,然后指定內部類名稱,想獲取什么,獲取什么。
打完收工。
ps:枚舉治理和常量治理結合使用,可能是我們系統開發時的最佳實踐,歡迎有更高效方法的朋友多多指教。
-------------------------------------------里程碑----------------------------------------------
至此,通過反射、自定義注解來解決系統中一些常見場景的使用問題告一段落,相關文章可參見:
-------------------------------------------里程碑----------------------------------------------