星期四, 12月 09, 2004

神奇的Reflection加上Registry Pattern

原由 :
十年前前寫結構矩陣或是Finite Element Method時,都是依照element的不同性質,代入一個相同的位移分析主程式。因此只要能將element代入主程式,就能夠在不動到主程式的情況下,動態增加element種類,也就是Reflection。問題就在於C++並沒有真正的late binding,到Smalltalk及後來的Java、.Net出現後才真正有此能力。若勉強用C++要寫,得用到function pointer之類的技巧,就又成為weak type。

以下是C的switch case範例,取自Alladin
ARRAY *elmt_cst ( ARRAY *p, int isw ) {
/* [a] : Jump to Task Case */

UNITS_SWITCH = CheckUnits();
switch(isw) {
case PROPTY: /* CST material properties */

..... add details of CST material properties ....

break;
case LOAD_MATRIX: /* compute equivalent nodal loads */
case STRESS: /* compute internal nodal loads */

..... fill in details here .... 

break;
case STIFF: /* compute element-level stiffness matrix */

..... put details of element stiffness matrix here ....

break;
case MASS_MATRIX: /* compute element-level mass matrix */

..... put details of element mass matrix here ....

break;
default:
break;
}

就算是使用Abstract Factory,仍然是省不了這一大串的select case,更別說source不給對方了。
最近寫另一個C#程式,剛好想起來這個問題,就可以用Registry加上Reflection。
利用唯一的一個Singleton Registry利用Reflection找出所有可用的類別,放到collection裏;而在系統動態載入某一類別(.class、.jar或是.dll)後,又可以再增加類別。
簡言之,Registry是利用Reflection加上Singleton及Iterator 3種pattern寫成的。

範例如下:

Element是我訂的Interface,裏頭有一個Abstract Method: Register
/// 
  /// 介面 Element,定義方法Register
  /// 
interface Element
{
 public void Register();
}

 

  /// 
  /// 利用Reflection註冊所有Element的子類別
  /// 
  private static void RegisterSubClasses()
  {
   Assembly a = typeof (SingletonInstrumentRegistry).Module.Assembly;
   Module[] modules = a.GetModules();
   for (int i = 0; i < modules.Length; i++)
   {
    Type[] types = modules[i].GetTypes();

    for (int j = 0; j < types.Length; j++)
    {
     if ((!types[j].IsAbstract) && types[j].IsSubclassOf(typeof (Element)))
     {
      // 產生子類別的 instance
      Object obj = types[j].InvokeMember(null,
                                         BindingFlags.DeclaredOnly |
                                          BindingFlags.Public |
                                          BindingFlags.Instance |
                                          BindingFlags.CreateInstance,
                                         null, null, null);

      types[j].InvokeMember("Register",
                            BindingFlags.DeclaredOnly |
                             BindingFlags.Public | BindingFlags.NonPublic |
                             BindingFlags.Instance | BindingFlags.InvokeMethod,
                             null, obj, null);
     }
    }
   }
  }
如果類別的方法又是Singleton,就可以做成非常方便又省記憶體的一種方式。但是,經實測後Reflection很慢,若類別太多,可以考慮做成查表法,將類別名稱儲存於xml檔案。