設計模式-8.代理模式

1.代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。用戶可以在代理對象中操作原對象。

2.代理模式的一般應用:提供了對目標對象的間接訪問方式,即通過代理訪問目標對象。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求。

3.代理模式分爲靜態代理和動態代理

  現在用一個小需求展示:給所有方法添加日誌打印功能 打印請求參數。

public class Calculator {

	//加
	public int add(int a, int b) {
		int result = a + b;
		return result;
	}

	//減
	public int subtract(int a, int b) {
		int result = a - b;
		return result;
	}

	//乘法、除法...
}

4.很多時候我們都是直接在方法體內打印

public class Calculator {

	//加
	public int add(int a, int b) {
		System.out.println("add方法開始...");
		int result = a + b;
		System.out.println("add方法結束...");
		return result;
	}

	//減
	public int subtract(int a, int b) {
		System.out.println("subtract方法開始...");
		int result = a - b;
		System.out.println("subtract方法結束...");
		return result;
	}

	//乘法、除法...
}

上面的方案是有問題的:

  1. 直接修改源程序,不符合開閉原則。應該對擴展開放,對修改關閉
  2. 如果Calculator有幾十個、上百個方法,修改量太大
  3. 存在重複代碼(都是在覈心代碼前後打印日誌)
  4. 日誌打印硬編碼在代理類中,不利於後期維護:比如你花了一上午終於寫完了,組長告訴你這個功能取消,於是你又要打開Calculator花十分鐘刪除日誌打印的代碼!

靜態代理實現日誌打印

目標接口

public interface Calculator {

	//加法
	public int add(int a,int b);
	//減法
	public int subtract(int a,int b);
	
	
}

 目標接口的實現類

package com.newland.draw.jdkproxy;

public class CalculatorImpl implements Calculator{

	@Override
	public int add(int a, int b) {
		// TODO Auto-generated method stub
		return a+b;
	}

	@Override
	public int subtract(int a, int b) {
		// TODO Auto-generated method stub
		return a-b;
	}

}

靜態代理對象類

package com.newland.draw.jdkproxy;

public class CalculatorStaticProxy implements Calculator{

	private Calculator target;
	
	public CalculatorStaticProxy(Calculator target) {
		this.target = target;
	}
	
	@Override
	public int add(int a, int b) {
		System.out.println("add方法開始...");
		int result = target.add(a, b);
		System.out.println("add方法結束...");
		return result;
	}

	@Override
	public int subtract(int a, int b) {
		System.out.println("subtract方法開始...");
		int result = target.subtract(a, b);
		System.out.println("subtract方法結束...");
		return result;
	}

	
}

客戶端

package com.newland.draw.jdkproxy;

public class Client {

	public static void main(String[] args) {
		
		//把目標對象通過構造器塞入代理對象
		Calculator calculator = new CalculatorStaticProxy(new CalculatorImpl());
		//代理對象調用目標對象方法完成計算,並在前後打印日誌
		calculator.add(1, 2);
		calculator.subtract(2, 1);

	}
}

輸出結果:

 

靜態代理的優點:可以在不修改目標對象的前提下,對目標對象進行功能的擴展和攔截。但是它也僅僅解決了上一種方案4大缺點中的第1點:

  1. 直接修改源程序,不符合開閉原則。應該對擴展開放,對修改關閉 √
  2. 如果Calculator有幾十個、上百個方法,修改量太大 ×
  3. 存在重複代碼(都是在覈心代碼前後打印日誌) ×
  4. 日誌打印硬編碼在代理類中,不利於後期維護:比如你花了一上午終於寫完了,組長告訴你這個功能取消,於是你又要打開Calculator花十分鐘刪除全部新增代碼!×

靜態代理的問題

上面案例中,代理類是我們事先編寫的,而且要和目標對象類實現相同接口。由於CalculatorImpl(目標對象)需要日誌功能,我們即編寫了CalculatorProxy(代理對象),並通過構造器傳入CalculatorImpl(目標對象),調用目標對象同名方法的同時添加增強代碼。

但是這裏有個問題!代理對象構造器的參數類型是Calculator,這意味着它只能接受Calculator的實現類對象,亦即我們寫的代理類CalculatorProxy只能給Calculator做代理,它們綁定死了!

如果現在我們系統需要全面改造,給其他類也添加日誌打印功能,就得爲其他幾百個接口都各自寫一份代理類...

 

自己手動寫一個類並實現接口實在太麻煩了。仔細一想,我們其實想要的並不是代理類,而是代理對象!那麼,能否讓JVM根據接口自動生成代理對象呢?

比如,有沒有一個方法,我傳入接口,它就給我自動返回代理對象呢?

答案是肯定的。這就引出了動態代理,動態代理分爲JDK代理和CGLIB動態代理,在設計模式當中就不細講了,我會放在spring中講解。

JDK動態代理地址:

CGLIB動態代理地址:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章