標籤:java反射
為了程式更好的維護和擴充,在物件導向思維的世界裡,首先是面向介面編程,然後我們應該把做什麼和怎麼做進行分離。
下面我將用一個開晚會的例子來示範一下,最終達到的效果是:工廠+反射+設定檔實現程式的靈活應用。會具體說明一下這個過程是怎麼來的,明白了這個,就會對反射和設定檔的結合更加深刻一些。
想要實現的功能是:晚會有一個唱歌、舞蹈、小品的節目單,具體各個節目的表演者只需要一個就可以,每一個表演介面都有兩個實作類別(表演者),通過用戶端調用不同的實作類別來實現不同的節目單。表演者就是“做什麼”,那麼“怎麼做”就是節目單了,首先來看看類圖結構:
下面放代碼,首先是介面,即各類表演:
<span style="font-size:14px;">/**跳舞介面*/</span><pre name="code" class="java">public interface Dancer { public void dance();} /**小品介面*/public interface Performer { public void performance();}/**歌唱介面*/ public interface Singer { public void sing();}
然後是各個實作類別,即表演者:
Dancer介面實作類別:public class YangLiPing implements Dancer { @Override public void dance() { System.out.println("楊麗萍跳舞:孔雀舞"); } } public class XiaoHuDui implements Dancer { @Override public void dance() { System.out.println("小虎隊跳舞:霹靂舞"); } }Performer介面實作類別:public class GongHanLin implements Performer { @Override public void performance() { System.out.println("鞏漢林表演:功夫令"); } }public class ZhaoBenShan implements Performer { @Override public void performance() { System.out.println("趙本山表演:賣拐"); } }Singer介面實作類別:public class ZhouHuaJian implements Singer { @Override public void sing() { System.out.println("周華健演唱:刀劍如夢"); } }public class WangFei implements Singer { @Override public void sing() { System.out.println("王菲演唱:我願意"); } }
用戶端調用方式:
<span style="font-size:14px;"> public static void main(String[] args) { //定義晚會流程 //演出:歌曲、舞蹈、表演 //第1種方法:單純使用多態 System.out.println("晚會開始=======>>"); Singer singer = new ZhouHuaJian(); singer.sing(); Performer performer = new ZhaoBenShan(); performer.performance(); Dancer dancer = new YangLiPing(); dancer.dance(); System.out.println("<<========晚會結束"); }</span>
代碼挺簡單,不過發現一個問題,作為節目召集人,在現實中基本上是不應該直接與表演者打交道的,而是與其所在的公司或者代理人進行交流,假設現在所有的表演者都同屬於一個娛樂公司,那麼我只需要從這個娛樂公司裡獲得我想要的那些表演者就可以了,那麼UML圖將會變為如下結構(Factory就代表這個娛樂公司):
Facotry代碼:
public classFactory { //提供準備歌手方法 public static Singer getSinger(){ return new ZhouHuaJian(); } //提供準備舞蹈方法 public static Dancer getDancer(){ return new YangLiPing(); } //提供準備表演方法 public static PerformergetPerformer(){ return new GongHanLin(); }}
那麼用戶端代碼就變成了這樣:
public staticvoidmain(String[] args) { //定義晚會流程 //演出:歌曲、小品、舞蹈 //第1種方法:使用工廠擷取各個表演種類 System.out.println("晚會開始=======>>"); Singersinger = Factory.getSinger(); singer.sing(); Performerperformer = Factory.getPerformer(); performer.performance(); Dancerdancer = Factory.getDancer(); dancer.dance(); System.out.println("<<========晚會結束");}
大家可以看到,在Factory代碼中我們發現還是將各個實作類別寫死了,也就是說娛樂公司強制某些表演者必須參加,但實際上很多時候有些表演者可能沒法參加,而另外一些閑著的表演者則可以參加,那麼我是不是應該在需要表演者的時候動態擷取有空參加更好一些呢?這個時候就需要用上設定檔+反射了。Factory代碼更改如下:
<span style="font-size:14px;">public class Factory { /**提供準備歌手方法*/ public static Singer getSinger(){ //讀取設定檔及擷取key對應的value StringclassName = ResourceBundle.getBundle("party").getString("Singer"); Singersinger = null; try { //將設定檔中讀取的完整類名通過反射擷取該類的執行個體 singer= (Singer)Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return singer; } /**提供準備舞蹈方法*/ public static Dancer getDancer(){ StringclassName = ResourceBundle.getBundle("party").getString("Dancer"); Dancerdancer = null; try { dancer= (Dancer)Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return dancer; } /**提供準備表演方法*/ public static PerformergetPerformer(){ StringclassName = ResourceBundle.getBundle("party").getString("Performer"); Performerperformer = null; try { performer= (Performer)Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return performer; }}</span>
設定檔party.properties中的代碼:
Singer = com.lc.reflect.demo3.person.WangFeiPerformer = com.lc.reflect.demo3.person.ZhaoBenShanDancer =com.lc.reflect.demo3.person.XiaoHuDui
這樣就能把代碼解耦了。仔細看的話,在Factory中的代碼3個方法基本上沒啥不同,除了變數和對象不同,發現了代碼的壞味道。那就重構吧:
public classFactory1 { /**提供準備歌手方法*/ public static Singer getSinger(){ Singersinger = null; return (Singer)getObject("Singer",singer); } /**提供準備舞蹈方法*/ public static Dancer getDancer(){ Dancerdancer = null; return (Dancer)getObject("Dancer",dancer); } /**提供準備表演方法*/ public static PerformergetPerformer(){ Performerperformer = null; return (Performer)getObject("Performer",performer); } /** * 擷取目標對象的方法 * @param objName 對象名稱 * @param obj 物件類型 * @return對象 */ public static Object getObject(StringobjName,Object obj){ //擷取設定檔中的目標物件路徑 StringclassName = ResourceBundle.getBundle("party").getString(objName); try { //擷取目標對象執行個體 obj= Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return obj; }}
這樣就簡潔多了。
學習一塊知識不僅僅要學習API是怎麼用的,還要與生活相結合,做出一個個簡單有效Demo,同時在做Demo的過程中,如果發現了編寫的代碼有值得重構的地方,一定不要停下思考的腳步,Just do it!雖然對反射的具體實現原理和過程還不太清楚,但首先會用才能有繼續研究的動力。繼續努力中~~
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java反射-簡單應用