在軟件開發中利用反射機制來減小包依賴

MD2File 是我之前寫的一個開源軟件,主要功能是「將markdown語法的文檔內容,導出爲word,pdf,HTML等的文件;也支持markdown轉HTML文本」。html

其中,導出爲word文檔,須要依賴POI,導出pdf須要依賴iText。其中,iText支持4.x版本和5.x版本。這裏就涉及到一個問題,我仍是舉例子說明下。java

若是用戶只須要一個功能,導出word文檔功能。那用戶總不能爲了這個功能,而引入一個無關的iText包吧。反之也是,用戶要導出PDF功能,卻須要引入POI包。git

可是,一開始個人代碼還真是這樣,若是不引入,會致使ClassNotFound異常,導致編譯不經過,沒法使用。markdown

設計上,我是經過一個工廠類,而後根據須要的文件名後綴,來生產不一樣的文檔Builder。代碼一開始是這麼寫的。ui

public class BuilderFactory{
	public static Decorator build(String ext) {
		DecoratorBuilder decoratorBuilder;
		if(ext.equalsIgnoreCase("docx")){
			decoratorBuilder = new DocxDecoratorBuilder();
		}else if(ext.equalsIgnoreCase("doc")){
			decoratorBuilder = new DocDecoratorBuilder();
		}else if(ext.equalsIgnoreCase("pdf")){
			if(itextVersion==PDF_ITEXT_5X){
				decoratorBuilder = new PDFDecoratorBuilder5x();
			}else{
				decoratorBuilder = new PDFDecoratorBuilder4x();;
			}
		}else if(ext.equalsIgnoreCase("html") ||ext.equalsIgnoreCase("htm")){
			decoratorBuilder = new HTMLDecoratorBuilder();
		}else{
			throw new RuntimeException("請確認輸出的文檔爲docx,doc,pdf,html的文檔格式");
		}
		Decorator decorator = decoratorBuilder.build();
		return decorator;
	}
}

這麼寫很大的問題,就是這個BuilderFactory依賴於DocxDecoratorBuilder,DocDecoratorBuilder,PDFDecoratorBuilder5x,PDFDecoratorBuilder4x,HTMLDecoratorBuilder。而這幾個Bulder又各自依賴於不一樣的第三方jar包。假設我沒引入POI包,DocxDecoratorBuilder,DocDecoratorBuilder報錯,BuilderFactory就編譯不經過了。.net

因而,就致使了咱們一開始說到的問題。設計

若是用戶只須要一個功能,導出word文檔功能。那用戶總不能爲了這個功能,而引入一個無關的iText包吧。反之也是,用戶要導出PDF功能,卻須要引入POI包。code

並且,還有個問題,若是用戶須要導出的是PDF文檔,那我怎麼知道用戶用的是iText 4.x包仍是iText 5.x包?htm

這時候是用Java的反射機制的好時機!先看改造後的代碼:開發

public class BuilderFactory{
	private static final int PDF_ITEXT_5X = 5;
	private static final int PDF_ITEXT_4X = 4;

	private static int itextVersion = PDF_ITEXT_4X;
	
	/**
	 * 檢查iText的版本
	 */
	static{
		String doc5x = "com.itextpdf.text.Document";
//		String doc4x = "com.lowagie.text.Document";
		try {
			Class.forName(doc5x);
			itextVersion = PDF_ITEXT_5X;
		} catch (ClassNotFoundException e) {
			itextVersion = PDF_ITEXT_4X;
		}
	}
	
	private static DecoratorBuilder initDecoratorBuilder(String className){
		try {
			@SuppressWarnings("rawtypes")
			Class clazz = Class.forName(className);
			return (DecoratorBuilder)clazz.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private static final String DOC_BUILDER_CLASS_NAME = "net.oschina.md2.export.builder.DocDecoratorBuilder";
	private static final String DOCX_BUILDER_CLASS_NAME = "net.oschina.md2.export.builder.DocxDecoratorBuilder";
	private static final String PDF_4X_BUILDER_CLASS_NAME = "net.oschina.md2.export.builder.PDFDecoratorBuilder4x";
	private static final String PDF_5X_BUILDER_CLASS_NAME = "net.oschina.md2.export.builder.PDFDecoratorBuilder5x";
	private static final String HTML_BUILDER_CLASS_NAME = "net.oschina.md2.export.builder.HTMLDecoratorBuilder";
	
	public static Decorator build(String ext) {
		DecoratorBuilder decoratorBuilder;
		if(ext.equalsIgnoreCase("docx")){
			decoratorBuilder = initDecoratorBuilder(DOCX_BUILDER_CLASS_NAME);
		}else if(ext.equalsIgnoreCase("doc")){
			decoratorBuilder = initDecoratorBuilder(DOC_BUILDER_CLASS_NAME);
		}else if(ext.equalsIgnoreCase("pdf")){
			if(itextVersion==PDF_ITEXT_5X){
				decoratorBuilder = initDecoratorBuilder(PDF_5X_BUILDER_CLASS_NAME);
			}else{
				decoratorBuilder = initDecoratorBuilder(PDF_4X_BUILDER_CLASS_NAME);
			}
		}else if(ext.equalsIgnoreCase("html") ||ext.equalsIgnoreCase("htm")){
			decoratorBuilder = initDecoratorBuilder(HTML_BUILDER_CLASS_NAME);
		}else{
			throw new RuntimeException("請確認輸出的文檔爲docx,doc,pdf,html的文檔格式");
		}
		Decorator decorator = decoratorBuilder.build();
		return decorator;
	}
	
}

如今,代碼裏咱們只是先定義了各個Builder實現的包路徑,而後經過

Class clazz = Class.forName(className);
(DecoratorBuilder)clazz.newInstance();

來初始化Builder,這樣就解決了BuilderFactory 依賴太重的問題。用戶須要用什麼功能,就只須要引入相應的jar包便可。若是用導出HTML功能或者markdown轉HTML功能,甚至任何其它包都不須要。

新的代碼還使用了個小技巧,來檢查用戶使用的iText版本問題。代碼就是下面這段

static{
		String doc5x = "com.itextpdf.text.Document";
//		String doc4x = "com.lowagie.text.Document";
		try {
			Class.forName(doc5x);
			itextVersion = PDF_ITEXT_5X;
		} catch (ClassNotFoundException e) {
			itextVersion = PDF_ITEXT_4X;
		}
	}

以上,就是本人在代碼開發中,使用反射來減小包依賴的小技巧。但願對看到本文的你有所幫助:-)

對了,若是恰好有markdown轉其它文檔的功能,能夠看下MD2File 這個小軟件,謝謝~

相關文章
相關標籤/搜索