Proxy代理人模式
Prerequisites:
代理人的作用是代替別人,去做那個人該做的事。
就好像現實世界中,我們買機票是透過旅行社去買,旅行社再跟航空公司買,而不是我們直接去跟航空公司買,但實際付錢的人是我們,不是旅行社。
而旅行社就是代理人,旅客是真正的類別,買機票是方法。
類別圖
- Agency 旅行社
- BuyTicket 介面
- Tourist 旅客
- Client 測試用的客戶端
首先要有一個介面(BuyTicket),旅行社Agency與旅客Tourist都要去實作介面pay()方法。
旅行社Agency的成員屬性要有旅客Tourist,使用建構子參數,把旅客傳入旅行社,所以是屬於共生共死的組合關係,菱形是實心。
旅行社Agency的pay()方法,實際上是呼叫旅客的pay()方法。
而測試時,是用旅行社去買機票,不是旅客去買機票。
程式碼
BuyTicket介面
1
2
3
public interface BuyTicket {
void pay();
}
旅客實作BuyTicket介面的pay方法。
1
2
3
4
5
6
public class Tourist implements BuyTicket{
@Override
public void pay() {
System.out.println("旅客提供信用卡 卡號付錢");
}
}
旅行社實作BuyTicket介面的pay方法,實際上是旅客付錢。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Agency implements BuyTicket{
// 成員屬性要有旅客Tourist
private Tourist tourist;
// 使用建構子參數,把旅客傳入旅行社
public Agency(Tourist tourist) {
this.tourist = tourist;
}
@Override
public void pay() {
// 實際上是呼叫旅客的pay()方法
tourist.pay();
}
}
Client測試
1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
// 建立旅客
Tourist tourist = new Tourist();
// 把旅客傳入旅行社的建構子
Agency agency = new Agency(tourist);
// 旅行社付錢,實際上旅客付的。
agency.pay();
}
}
旅客提供信用卡 卡號付錢
Thread
執行緒也是代理人模式,執行緒都會實作Runnable代理介面。
以下是執行緒代理人。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ThreadProxy implements Runnable{
private Runnable target;
@Override
public void run() {
// 真正呼叫的是target的run方法。
if (target != null) {
target.run();
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
// 啟動執行緒
public void start() {
start0();
}
private void start0() {
run();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test3 {
public static void main(String[] args) {
// 使用匿名內部類的方法,建立一個匿名的實作Runnable介面的物件
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("執行r1的run方法");
}
};
// 把real class傳入建構子
ThreadProxy threadProxy = new ThreadProxy(r1);
// 真正執行的是匿名內部類別的run方法。
threadProxy.start();
}
}
Java動態代理介面
使用反射,傳回代理介面,呼叫代理介面的方法,也可以呼叫到target的方法。
注意!傳回值為介面!不是具體的子類別。
類別圖
- ProxyFactory 代理工廠,建構子傳入真正類別
- ProxyInterface 代理介面
- target 真正的類別,實作ProxyInterface中的method方法。
- Client 使用ProxyFactory傳回代理介面。
程式碼
ProxyInterface與target跟先前的一模一樣。
使用java.lang.reflect.Proxy
Proxy.newProxyInstance(classLoader(), Interfaces(), InvocationHandler());
- 第一個參數為類別的classLoader
- 第二個參數為類別的所有介面interfaces
- 第三個參數為InvocationHandler()
透過反射的方法,建立代理人
ProxyFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 反射代理人需要的類別
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
// 類型為Object
private Object target;
// 參數類型為Object
public ProxyFactory(Object target) {
this.target = target;
}
// 傳回值類型是Object
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 取得classloader
target.getClass().getInterfaces(), // 取得介面
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 第1個參數為target真正的類別
Object instance = method.invoke(target, args);
return instance;
}
});
}
}
Client
1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
// 類型為ProxyInterface介面,使用子類別target的建構子建立介面
ProxyInterface target = new target();
// 轉型成ProxyInterface介面,參數代入target
ProxyInterface proxyClass = (ProxyInterface) new ProxyFactory(target).getProxyInstance();
// 使用介面的方法
proxyClass.method1();
}
}
真正類別要做的事