首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

重構實戰:利用反射合併重複方法,打造通用樹狀結構建構器

     編輯:Java 相關知識

在開發權限管理系統時,常需將扁平的資料轉換為樹狀結構。原始碼中存在三個高度相似的方法(test、test2、test3),分別處理 Button→Model、Model→SubMenu、SubMenu→MainMenu 的層級關聯。本文展示如何利用 Apache Commons BeanUtils 的反射機制,將這三個方法重構為單一通用方法 testC,提升程式碼復用性與維護彈性。

重構前:重複的層級建構邏輯

原始測試類別 TestLogin 包含三個幾乎相同的私有方法:

public void test(Map<String,Model> modelmap, Button b) {
    Model m = b.getModel();
    if (!modelmap.containsKey(m.getId()))
        modelmap.put(m.getId(), m);
    modelmap.get(m.getId()).getButtons().add(b);
}

public void test2(Map<String,Menu> menumap, Model m) {
    Menu menu = m.getMenu();
    if (!menumap.containsKey(menu.getId()))
        menumap.put(menu.getId(), menu);
    menumap.get(menu.getId()).getModels().add(m);
}

public void test3(Map<String,Menu> menumap, Menu smenu) {
    Menu fmenu = smenu.getMenu();
    if (!menumap.containsKey(fmenu.getId()))
        menumap.put(fmenu.getId(), fmenu);
    menumap.get(fmenu.getId()).getMenus().add(smenu);
}

三者邏輯一致:取得父物件、以父物件 ID 為 Key 存入 Map、將子物件加入父物件的集合屬性。差異僅在於參數型別、父屬性名稱、子集合屬性名稱。

重構後:通用反射方法

引入 BeanUtils.getDeclaredProperty 透過反射動態存取屬性,定義通用方法:

@SuppressWarnings("unchecked")
public void testC(Map map, Object t, String parent, String childrenName) {
    try {
        Object tParent = BeanUtils.getDeclaredProperty(t, parent);
        Object tParentId = BeanUtils.getDeclaredProperty(tParent, "id");
        if (!map.containsKey(tParentId)) {
            map.put(tParentId, tParent);
        }
        Object parentObj = map.get(tParentId);
        Collection childrenCollection = (Collection) BeanUtils.getDeclaredProperty(parentObj, childrenName);
        childrenCollection.add(t);
    } catch (IllegalAccessException | NoSuchFieldException e) {
        e.printStackTrace();
    }
}

BeanUtils.getDeclaredProperty 實作細節:

static public Object getDeclaredProperty(Object object, String propertyName)
    throws IllegalAccessException, NoSuchFieldException {
    Assert.notNull(object);
    Assert.hasText(propertyName);
    Field field = object.getClass().getDeclaredField(propertyName);
    return getDeclaredProperty(object, field);
}

呼叫端簡化

testall 方法大幅精簡,僅需傳入對應的屬性名稱:

public void testall(List<Button> buttonList) {
    for (Button button : buttonList) {
        testC(modelmap, button, "model", "buttons");
    }
    for (Model model : modelmap.values()) {
        testC(smenumap, model, "menu", "models");
    }
    for (Menu menu : smenumap.values()) {
        testC(fmenumap, menu, "menu", "menus");
    }
    // 列印樹狀結構驗證...
}

重構效益分析

  • 消除重複:三個方法合併為一,程式碼行數減少約 60%
  • 擴充性:未來新增層級(如 SubSubMenu)只需多呼叫一次 testC,無需新增方法
  • 型別安全權衡:犧牲編譯期型別檢查,換取執行期靈活性;建議搭配單元測試確保正確性
  • 效能考量:反射有微小效能開銷,但在建構樹狀結構這類非高頻操作中可忽略不計

適用場景與注意事項

此模式適用於「固定演算法、可變屬性名稱」的場景。使用時請注意:

  1. 確保所有參與物件皆有 id 屬性且可透過 Getter 存取
  2. 父屬性(parent)與子集合屬性(childrenName)名稱必須準確對應實際欄位
  3. 集合屬性必須已初始化(如 new ArrayList()),否則會拋出 NullPointerException
  4. 生產環境建議將例外處理改為拋出自訂例外或記錄日誌,避免 printStackTrace
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved