利用反射實作類別的動態載入
Bromon原創 請尊重著作權
最近在成都寫一個移動增值項目,俺負責後台server端。功能很簡單,手機使用者通過GPRS開啟Socket與伺服器串連,我則根據使用者傳過來的資料做出響應。做過類似項目的兄弟一定都知道,首先需要定義一個類似於MSNP的通訊協議,不過今天的話題是如何把這個系統設計得具有高度的擴充性。由於這個項目本身沒有進行過較為完善的客戶溝通和需求分析,所以以後肯定會有很多功能上的擴充,通訊協議肯定會越來越龐大,而我作為一個不那麼勤快的人,當然不想以後再去修改寫好的程式,所以這個項目是實踐物件導向設計的好機會。
首先定義一個介面來隔離類:
package org.bromon.reflect;
public interface Operator
{
public java.util.List act(java.util.List params)
}
根據設計模式的原理,我們可以為不同的功能編寫不同的類,每個類都繼承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;
}
}
我們還可以寫其他很多類,但是有個問題,介面是無法執行個體化的,我們必須手動控制具體執行個體化哪個類,這很不爽,如果能夠嚮應用程式傳遞一個參數,讓自己去選擇執行個體化一個類,執行它的act方法,那我們的工作就輕鬆多了。
很幸運,我使用的是Java,只有Java才提供這樣的反射機制,或者說內省機制,可以實現我們的無理要求。編寫一個設定檔emp.properties:
#成功響應
1000=Success
#向客戶發送普通簡訊
2000=Load
#客戶向伺服器發送普通簡訊
3000=Store
檔案中的鍵名是客戶將發給我的訊息頭,客戶發送1000給我,那麼我就執行Success類的act方法,類似的如果發送2000給我,那就執行Load類的act方法,這樣一來系統就完全符合開閉原則了,如果要添加新的功能,完全不需要修改已有代碼,只需要在設定檔中添加對應規則,然後編寫新的類,實現act方法就ok,即使我棄這個項目而去,它將來也可以很好的擴充。這樣的系統具備了非常良好的擴充性和可插入性。
下面這個例子體現了動態載入的功能,程式在執行過程中才知道應該執行個體化哪個類:
package org.bromon.reflect.*;
import java.lang.reflect.*;
public class TestReflect
{
//載入設定檔,查詢訊息頭對應的類名
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;
}
//針對訊息作出響應,利用反射匯入對應的類
public String response(String header,List content)
{
String result=null;
String s=null;
try
{
/*
* 匯入屬性檔案emp.properties,查詢header所對應的類的名字
* 通過反射機制動態載入匹配的類,所有的類都被Operator介面隔離
* 可以通過修改屬性檔案、添加新的類(繼承MsgOperator介面)來擴充協議
*/
s="org.bromon.reflect."+this.loadProtocal(header);
//載入類
Class c=Class.forName(s);
//建立類的案例
Operator mo=(Operator)c.newInstance();
//構造參數列表
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;
//調用方法並且獲得返回
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],”訊息內容”);
}
}
測試一下:java TestReflect 1000
這個程式是針對Operator編程的,所以無需做任何修改,直接提供Load和Store類,就可以支援2000、3000做參數的調用。
有了這樣的內省機制,可以把介面的作用發揮到極至,設計模式也更能體現出威力,而不僅僅供我們飯後閑聊。