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

用 Java 動態代理實現記憶化功能:通用緩存包裝器 Memoizer 詳解

     編輯:Java 相關知識

什麼是記憶化?

記憶化源於 Lisp、Python、Perl 等語言,是一種設計模式:緩存函數的計算結果,相同輸入直接返回緩存值,避免重複計算。對於計算密集型方法(如計算圓周率二進制位),能顯著提升性能,且屬可復用橫切關注點。

從硬編碼緩存到裝飾器模式

階段 1:侵入式緩存(反模式)

在 PiBinaryDigitsCalculator 內部加 HashMap 緩存。缺點:業務邏輯與緩存邏輯耦合,難測試、難移除、不可復用。

階段 2:裝飾器模式分離關注點

定義介面 BinaryDigitsCalculator,實現類 PiBinaryDigitsCalculator 專注算法,包裝類 CachingBinaryDigitsCalculator 持有被包裝實例並添加緩存。優點:職責分離,易測試(可注入 Mock 實現驗證緩存行為),可熱插拔。

動態代理:通用化緩存包裝器

JDK 1.3+ 引入 java.lang.reflect.Proxy,運行時動態生成實現指定介面的代理類。我們編寫通用 Memoizer 實現 InvocationHandler,一次編寫、處處復用:

BinaryDigitsCalculator calculator = (BinaryDigitsCalculator) Memoizer.memoize(new PiBinaryDigitsCalculator());

Memoizer 核心實現

  • memoize(Object target):創建代理實例,代理目標物件實現的所有介面。
  • invoke(proxy, method, args):攔截方法調用。
    • void 方法直接執行,不緩存。
    • 非 void 方法:以 (Method, 參數列表) 為 Key 查詢緩存(Map<Method, Map<List<Object>, Object>>),命中則返回,否則反射調用目標方法、存入緩存並返回。
  • Collections.synchronizedMap。

適用條件與限制

被記憶化的方法必須滿足:

  1. 冪等性:相同輸入必然產生相同輸出(無隨機、無時間依賴)。
  2. 無副作用:不修改狀態、不發送 IO、不拋出依賴狀態的異常(void 方法自動排除)。
  3. 參數不可變:參數作為 Map Key,若調用後發生變異會導致 Key 不匹配或錯誤命中。

性能權衡

反射調用開銷:JDK 1.4 約為直接調用 1/2(JDK 1.3 為 1/40)。僅當緩存命中帶來的計算節省遠超反射開銷時才值得使用。建議:基準測試對比,輕量級方法不適用。

擴展:緩存策略工廠

支援 LRU、大小限制等策略:

Memoizer.memoize(target, new LruCacheFactory(1000));

結語

Memoizer 以 ~100 行代碼實現了通用、可配置、線程安全的記憶化組件,是 Java 開發者工具箱中的利器。正確識別適用場景,度量收益,謹慎使用反射。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved