反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。
利用反射實(shí)現(xiàn)類的動態(tài)加載
Bromon原創(chuàng) 請尊重版權(quán)
最近在成都寫一個移動增值項(xiàng)目,俺負(fù)責(zé)后臺server端。功能很簡單,手機(jī)用戶通過GPRS打開Socket與服務(wù)器連接,我則根據(jù)用戶傳過來的數(shù)據(jù)做出響應(yīng)。做過類似項(xiàng)目的兄弟一定都知道,首先需要定義一個類似于MSNP的通訊協(xié)議,不過今天的話題是如何把這個系統(tǒng)設(shè)計得具有高度的擴(kuò)展性。由于這個項(xiàng)目本身沒有進(jìn)行過較為完善的客戶溝通和需求分析,所以以后肯定會有很多功能上的擴(kuò)展,通訊協(xié)議肯定會越來越龐大,而我作為一個不那么勤快的人,當(dāng)然不想以后再去修改寫好的程序,所以這個項(xiàng)目是實(shí)踐面向?qū)ο笤O(shè)計的好機(jī)會。
首先定義一個接口來隔離類:
package org.bromon.reflect;
public interface Operator
{
public java.util.List act(java.util.List params)
}
根據(jù)設(shè)計模式的原理,我們可以為不同的功能編寫不同的類,每個類都繼承Operator接口,客戶端只需要針對Operator接口編程就可以避免很多麻煩。比如這個類:
package org.bromon.reflect.*;
public class Success implements Operator
{
public java.util.List act(java.util.List params)
{
List result=new ArrayList();
result.add(new String(“操作成功”));
return result;
}
}
我們還可以寫其他很多類,但是有個問題,接口是無法實(shí)例化的,我們必須手動控制具體實(shí)例化哪個類,這很不爽,如果能夠向應(yīng)用程序傳遞一個參數(shù),讓自己去選擇實(shí)例化一個類,執(zhí)行它的act方法,那我們的工作就輕松多了。
很幸運(yùn),我使用的是Java,只有Java才提供這樣的反射機(jī)制,或者說內(nèi)省機(jī)制,可以實(shí)現(xiàn)我們的無理要求。編寫一個配置文件emp.properties:
#成功響應(yīng)
1000=Success
#向客戶發(fā)送普通文本消息
2000=Load
#客戶向服務(wù)器發(fā)送普通文本消息
3000=Store
文件中的鍵名是客戶將發(fā)給我的消息頭,客戶發(fā)送1000給我,那么我就執(zhí)行Success類的act方法,類似的如果發(fā)送2000給我,那就執(zhí)行Load類的act方法,這樣一來系統(tǒng)就完全符合開閉原則了,如果要添加新的功能,完全不需要修改已有代碼,只需要在配置文件中添加對應(yīng)規(guī)則,然后編寫新的類,實(shí)現(xiàn)act方法就ok,即使我棄這個項(xiàng)目而去,它將來也可以很好的擴(kuò)展。這樣的系統(tǒng)具備了非常良好的擴(kuò)展性和可插入性。
下面這個例子體現(xiàn)了動態(tài)加載的功能,程序在執(zhí)行過程中才知道應(yīng)該實(shí)例化哪個類:
package org.bromon.reflect.*;
import java.lang.reflect.*;
public class TestReflect
{
//加載配置文件,查詢消息頭對應(yīng)的類名
private String loadProtocal(String header)
{
String result=null;
try
{
Properties prop=new Properties();
FileInputStream fis=new FileInputStream("emp.properties");
prop.load(fis);
result=prop.getProperty(header);
fis.close();
}catch(Exception e)
{
System.out.println(e);
}
return result;
}
//針對消息作出響應(yīng),利用反射導(dǎo)入對應(yīng)的類
public String response(String header,String content)
{
String result=null;
String s=null;
try
{
/*
* 導(dǎo)入屬性文件emp.properties,查詢header所對應(yīng)的類的名字
* 通過反射機(jī)制動態(tài)加載匹配的類,所有的類都被Operator接口隔離
* 可以通過修改屬性文件、添加新的類(繼承MsgOperator接口)來擴(kuò)展協(xié)議
*/
s="org.bromon.reflect."+this.loadProtocal(header);
//加載類
Class c=Class.forName(s);
//創(chuàng)建類的事例
Operator mo=(Operator)c.newInstance();
//構(gòu)造參數(shù)列表
Class params[]=new Class[1];
params[0]=Class.forName("java.util.List");
//查詢act方法
Method m=c.getMethod("act",params);
Object args[]=new Object[1];
args[0]=content;
//調(diào)用方法并且獲得返回
Object returnObject=m.invoke(mo,args);
}catch(Exception e)
{
System.out.println("Handler-response:"+e);
}
return result;
}
public static void main(String args[])
{
TestReflect tr=new TestReflect();
tr.response(args[0],”消息內(nèi)容”);
}
}
測試一下:java TestReflect 1000
這個程序是針對Operator編程的,所以無需做任何修改,直接提供Load和Store類,就可以支持2000、3000做參數(shù)的調(diào)用。
有了這樣的內(nèi)省機(jī)制,可以把接口的作用發(fā)揮到極至,設(shè)計模式也更能體現(xiàn)出威力,而不僅僅供我們飯后閑聊。
?