1. 程式人生 > >設計模式之代理模式(java)

設計模式之代理模式(java)

1、概念

為其他物件提供一種代理以控制對這個物件的訪問。在某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。

2、UML類圖

 

3、java程式碼實現

 抽象角色:表演介面(Act)

public interface Act {

	void act();

}

具體角色:演員類(Actor),實現表演介面(Act)

public class Actor implements Act {

	private String name;

	@Override
	public void act() {
		System.out.println("我是演員,我會噴火!!!");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

代理角色:代理類(StaticProxy),實現表演介面(Act),維護一個表演介面(Act)的引用

public class StaticProxy implements Act {

	private Act target;

	public StaticProxy(Act target) {
		this.target = target;
	}

	@Override
	public void act() {
		System.out.println("我是代理人,下面是演員的表演!!!");
		target.act();
	}

}

測試

@Test
public void testStaticProxy(){
	Act target = new Actor();
	StaticProxy proxy = new StaticProxy(target);
	proxy.act();
}

測試結果

我是代理人,下面是演員的表演!!!
我是演員,我會噴火!!!

以上程式碼用"代理模式"實現了的一個簡單案列,這種代理模式叫做"靜態代理",它的一個明顯的缺點就是介面發生變化,代理物件的程式碼也要進行相應的維護。除了"靜態代理"外,java還存在其他兩種"代理模式",分別是"動態代理"和"Cglib代理"

動態代理(jdk代理或介面代理):目標物件必須實現一個或多個介面

public class ProxyFactory {

	private Object target;

	public ProxyFactory(Object target) {
		this.target = target;
	}

	public Object newProxyInstance(){
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (Object proxy, Method method, Object[] args) -> {
				System.out.println("動態代理(JDK代理)");
				Object returnValue = method.invoke(target, args);
				return returnValue;
			}
		);
	}

}

測試及測試結果

@Test
public void testJdkProxy(){
	Act target = new Actor();
	ProxyFactory factory = new ProxyFactory(target);
	Act act = (Act) factory.newProxyInstance();
	act.act();
}
動態代理(JDK代理)
我是演員,我會噴火!!!

Cglib代理:無需實現介面

舞者類(Dancer):沒有實現任何介面

public class Dancer {

	public void dance() {
		System.out.println("我是舞者,我會跳舞!!!");
	}

}

Cglib代理類

public class CglibProxy implements MethodInterceptor {

	private Object target;

	public CglibProxy(Object target) {
		this.target = target;
	}

	public Object newProxyInstance() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		System.out.println("Cglib代理");
		Object returnValue = method.invoke(target, objects);
		return returnValue;
	}

}

測試及測試結果

@Test
public void testCglibProxy() {
	Dancer dancer = new Dancer();
	CglibProxy cglibProxy = new CglibProxy(dancer);
	Dancer proxy = (Dancer) cglibProxy.newProxyInstance();
	proxy.dance();
}
Cglib代理
我是舞者,我會跳舞!!!

4、總結

三種代理模式各有優缺點,主要在於目標物件是否實現了介面。

例如:在Spring中,加入容器的目標物件實現了介面,就用jdk代理;沒有就用cglib代理。