Java 中實例化類的動作,你是否還是一成不變 new 對應對象呢?
經手的項目多了,代碼編寫量自然會增加,漸漸的會對設計模式產生感覺。
怎樣使書寫出來的類實例化動作,高內聚,低耦合,又兼具一定的擴展能力呢?
本文試圖從幾段鮮活的代碼入手,給大家呈現不一樣的 Java 實例化類。
下面代碼取自 com.google.zxing 源碼實現:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType, ?> hints) throws WriterException { Object writer; switch (format.ordinal()) { case 1 : writer = new AztecWriter(); break ; case 2 : writer = new CodaBarWriter(); break ; case 3 : writer = new Code39Writer(); break ; case 4 : case 10 : case 13 : case 14 : default : throw new IllegalArgumentException( "No encoder available for format " + format); case 5 : writer = new Code128Writer(); break ; case 6 : writer = new DataMatrixWriter(); break ; case 7 : writer = new EAN8Writer(); break ; case 8 : writer = new EAN13Writer(); break ; case 9 : writer = new ITFWriter(); break ; case 11 : writer = new PDF417Writer(); break ; case 12 : writer = new QRCodeWriter(); break ; case 15 : writer = new UPCAWriter(); break ; case 16 : writer = new UPCEWriter(); } return ((Writer)writer).encode(contents, format, width, height, hints); } |
其中的 BarcodeFormat 是這樣的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public enum BarcodeFormat { AZTEC, CODABAR, CODE_39, CODE_93, CODE_128, DATA_MATRIX, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, RSS_14, RSS_EXPANDED, UPC_A, UPC_E, UPC_EAN_EXTENSION; private BarcodeFormat() { } } |
源碼提供的功能是將信息通過幾種不同類型條形碼 Wirter 輸出為位矩陣,然后輸出到圖片上面,形成隨處可見的各種類型的條形碼。
BitMatrix bitMatrix = new MultiFormatWriter().encode(_text, BarcodeFormat.QR_CODE, qrcodeWidth, qrcodeHeight, hints);
MatrixToImageWriter.writeToFile(bitMatrix, qrcodeFormat, QrcodeFile);
源碼作者在這里使用了JDK 1.5 中引入的新特性 enum 枚舉類,編寫了BarcodeFormat類,其中定義了不同類型的條形碼的屬性。
調用 MultiFormatWriter.encode() 根據入參 BarcodeFormat.xx 在枚舉類中的序號,來實例化具體的類。
1
2
3
4
5
6
7
8
9
10
11
|
switch (format.ordinal()) { case 1 : writer = new AztecWriter(); break ; case 2 : writer = new CodaBarWriter(); break ; case 3 : writer = new Code39Writer(); break ; ............... |
這些條形碼 Writer 類,同時都實現了抽象接口 Writer 的 兩個encode()方法。
1
2
3
4
5
|
public interface Writer { BitMatrix encode(String var1, BarcodeFormat var2, int var3, int var4) throws WriterException; BitMatrix encode(String var1, BarcodeFormat var2, int var3, int var4, Map<EncodeHintType, ?> var5) throws WriterException; } |
具體的條形碼 Wirter 類內部根據不同類型的條形碼規則,進行不同的邏輯。
使用者不需要過多的關注內部的實現,需要產生什么樣子的條形碼,入參選用合適的條形碼類型即可,筆者上述的例子里面實現的是二維碼。
在來看經典 MVC 框架 Webwork 動態實例化類的一段方法代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
private static Configuration getDefaultConfiguration () { if (defaultImpl == null ) { defaultImpl = new DefaultConfiguration(); try { String className = getString( "webwork.configuration" ); if (!className.equals(defaultImpl.getClass().getName())) { try { defaultImpl = (Configuration) ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLoader().loadClass(className)); } catch (Exception e) { LOG.error( "Could not instantiate configuration" , e); } } return defaultImpl; } catch (IllegalArgumentException localIllegalArgumentException) { } } } |
源碼取自 webwork-core,可能很多看客老爺沒有聽聞 Webwork, 但是對 Struts 應該是如雷貫耳,Struts2 核心改寫自 Webwork。
上述源碼提供的功能為實例化用戶自己定義的 配置文件讀取類,該定義是在配置文件當中。
源碼作者在這里使用 Thread.currentThread().getContextClassLoader().loadClass(className) 線程中類加載器,動態實例化自定義配置文件讀取類,可謂是效率最高的一種做法。
類加載器的委托鏈:SystemClassloader -> ExtensionClassloader -> BootstrapClassloader
委派鏈左邊的ClassLoader就可以很自然的使用右邊的ClassLoader所加載的類,類加載的機制為判斷自已是否加載該類,沒有在詢問上級。
而這三個類加載器分別對應著編譯器去尋找類文件的優先級別和不同的路徑:
- BootClassLoader 它是用C++編寫的,從%jre%/lib目錄中加載類,或者運行時用-Xbootclasspath指定目錄來加載。是編譯器最優先尋找class的地方
- ExtClassLoader 從%jre%/lib/ext目錄加載類,或者運行時用-Djava.ext.dirs制定目錄來加載。是編譯器次優先尋找class的地方
- SystemClassloader 也就是我們常說的AppClassloader ,它對應當前路徑,所以也是編譯器默認找class的地方。
平時項目中使用的 Class.forname() 會從 BootstrapClassloader 開始詢問,是最消耗資源的。
源碼作者在這里采用線程類加載器,對應為 SystemClassloader ,效率無疑是最高的。