學習代理的設計模式的時候,經常碰到的一個經典場景就是想統計某個方法的執行時間。
1 靜態代理模式的產生
需求1. 統計方法執行時間
統計方法執行時間,在很多API/性能監控中都有這個需求。
下面以簡單的計算器為例子,計算加法耗時。代碼如下:
public Long add(Integer a, Integer b) {long result = 0;for(int i=0; i<100000000; i++) {result += i + a + b;}return result;
}
方法很簡單,就是計算兩數之和。這里為了方便統計耗時,所以循環了很多次。
統計耗時,那么實現起來也很簡單:
public Long add(Integer a, Integer b) {long start = System.currentTimeMillis();long result = 0;for(int i=0; i<100000000; i++) {result += i + a + b;}long end = System.currentTimeMillis();System.out.println("cost time: " + (end - start) + "ms");return result;}
統計代碼和業務代碼,雜糅在一起,是否是感覺有點混亂,有沒有一種方法:在不影響原有業務邏輯情況下實現統計耗時的功能?
需求2.不影響原有業務邏輯,實現方法的耗時統計
很快我們想到一種方法,那就是定義一個父類,專門做耗時統計,業務代碼通過抽象方法定義,子類擴展具體的業務方法即可。
代碼如下:
public abstract class AbstractTime {public Long tickTock(Integer a, Integer b) {long start = System.currentTimeMillis();long result = calculate(a,b);long end = System.currentTimeMillis();System.out.println("cost time: " + (end - start) + "ms");return result;}protected abstract Long add(Integer a, Integer b);
}
計算加法的耗時統計如下:
?
public class AddTime extends AbstractTime {@Overridepublic Long add(Integer a, Integer b) {long result = 0;for(int i=0; i<100000000; i++) {result += i + a + b;}return result;}public static void main(String[] args){AddTime addTime = new AddTime();addTime.tickTock(1, 2);}
}
很好,實現無侵入式統計方法耗時,完成既定目標。
那么問題又來了,假如我們這個方法還需要繼承另外一個類,這個時候怎么辦呢?
需求3:使用接口方式,實現無侵入式統計方法耗時
我們先把需要實現的業務邏輯,通過接口的方式封裝起來,定義一個接口。
public interface Calculator {Long add(Integer a, Integer b);
}
實現業務接口的方法類:
public class AddCalculator implements Calculator {@Overridepublic Long calculate(Integer a, Integer b) {long result = 0;for(int i=0; i<100000000; i++) {result += i + a + b;}return result;}
}
使用接口,無侵入式實現方法耗時統計的方案就是,設計模式里的經典方案:代理模式。
這里使用代理模式,實現的代理類如下:
public class AddCalculatorProxy implements Calculator {private Calculator calculator;public AddCalculatorProxy(Calculator calculator) {this.calculator = calculator;}@Overridepublic Long calculate(Integer a, Integer b) {long start = System.currentTimeMillis();// 具體的業務邏輯類Long result = calculator.add(a, b);long end = System.currentTimeMillis();System.out.println("cost time: " + (end - start) + "ms");return result;}public static void main(String[] args) {Calculator calculator = new AddCalculator();Calculator proxy = new AddCalculatorProxy(calculator);proxy.add(1, 2);}
}
這就是靜態代理模式的推演過程。
2. 靜態代理模式是什么
靜態代理模式是一種設計模式,用于在不修改目標對象的前提下,通過代理對象來控制對目標對象的訪問。以下是關于靜態代理模式的詳細說明:
2.1. 定義
靜態代理模式中,代理類和目標類實現相同的接口,代理類持有目標類的實例,并通過代理類間接調用目標類的方法。代理類可以在方法執行前后添加額外的邏輯。
2.2. 特點
接口:目標類和代理類都實現了同一個接口。
代理類:代理類持有一個目標類的引用,并在其方法中調用目標類的方法。
擴展性:可以在不修改目標類的情況下,通過代理類添加額外的功能(如日志記錄、性能監控等)。
2.3. 代碼分析
根據上面的例子,以下是對靜態代理模式的實現分析:
接口定義
public interface Calculator {Long add(Integer a, Integer b);
}
定義了一個 Calculator 接口,包含一個 add 方法。
目標類:
public class AddCalculator implements Calculator {@Overridepublic Long calculate(Integer a, Integer b) {long result = 0;for(int i=0; i<100000000; i++) {result += i + a + b;}return result;}
}
AddCalculator 是目標類,實現了 Calculator 接口。
提供了具體的業務邏輯(例如計算兩個數的和并進行循環累加)。
代理類
public class AddCalculatorProxy implements Calculator {private Calculator calculator;public AddCalculatorProxy(Calculator calculator) {this.calculator = calculator;}@Overridepublic Long calculate(Integer a, Integer b) {long start = System.currentTimeMillis();// 調用目標類的業務邏輯Long result = calculator.add(a, b);long end = System.currentTimeMillis();System.out.println("cost time: " + (end - start) + "ms");return result;}
}
AddCalculatorProxy 是代理類,也實現了 Calculator 接口。
持有一個 Calculator 類型的目標類實例。
在 calculate 方法中,代理類在調用目標類的 add 方法前后添加了時間統計的邏輯。
測試代碼
public static void main(String[] args) {Calculator calculator = new AddCalculator();Calculator proxy = new AddCalculatorProxy(calculator);proxy.add(1, 2);
}
創建目標類實例 AddCalculator。
使用代理類 AddCalculatorProxy 包裝目標類實例。
調用代理類的 add 方法時,會自動執行代理類中的額外邏輯(如性能統計)。
2.4. 優點
職責分離:將核心業務邏輯與附加功能分離,符合單一職責原則。
增強功能:可以在不修改目標類的情況下,通過代理類添加新的功能。
2.5. 缺點
代碼膨脹:每新增一個目標類,就需要創建一個對應的代理類,可能導致代碼量增加。
靈活性不足:代理類和目標類必須實現相同的接口,缺乏動態性。
2.6. 總結
靜態代理模式適用于需要在目標類的基礎上擴展功能的場景。它通過代理類封裝目標類的行為,同時保持接口的一致性。例子代碼很好地展示了靜態代理模式的應用,通過代理類實現了性能監控的功能。