原理或定義
提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象.這樣做的好處是: 可以在目標對象實現的基礎上, 增強額外的功能操作, 即擴展目標對象的功能。
代理模式又分爲靜態代理和動態代理。靜態代理是由程序猿創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。動態代理是在程序運行時,通過運用反射機制動態的創建而成。
結構
抽象角色:聲明真實對象和代理對象的共同接口。
代理角色:代理對象角色內部含有對真實對象的引用,從而可以操作真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當於對真實對象進行封裝。
真實角色:代理角色所代表的真實對象,是我們最終要引用的對象。
類圖
案例與代碼
本文使用遠程糖果機監控項目作爲遠程代理的案例
監控糖果機:地點、糖果庫存和當前狀態
遠程代理:遠程對象的本地代表,通過它可以讓遠程對象當本地對象來調用。遠程代理通過網絡和真正的遠程對象溝通信息。
RMI遠程方法調用是計算機之間通過網絡實現對象調用的一種通訊機制。
使用這種機制,一臺計算機上的對象可以調用另外 一臺計算機上的對象來獲取遠程數據。
RMI開發步驟:
製作遠程接口:接口文件
遠程接口的實現:Service文件
RMI服務端註冊,開啓服務
RMI代理端通過RMI查詢到服務端,建立聯繫,通過接口調用遠程方法
被監控的機器類:
public class CandyMachine extends UnicastRemoteObject implements CandyMachineRemote{
State mSoldOutState;
State mOnReadyState;
State mHasCoin;
State mSoldState;
State mWinnerState;
private String location="";
private State state;
private int count = 0;
public CandyMachine(String location,int count) throws RemoteException{
this.location=location;
this.count = count;
mSoldOutState = new SoldOutState(this);
mOnReadyState = new OnReadyState(this);
mHasCoin = new HasCoin(this);
mSoldState = new SoldState(this);
mWinnerState = new WinnerState(this);
if (count > 0) {
state = mOnReadyState;
} else {
state = mSoldOutState;
}
}
public String getLocation()
{
return location;
}
public void setState(State state) {
this.state = state;
}
public void insertCoin() {
state.insertCoin();
}
public void returnCoin() {
state.returnCoin();
}
public void turnCrank() {
state.turnCrank();
state.dispense();
}
void releaseCandy() {
// TODO Auto-generated method stub
if (count > 0) {
count = count - 1;
System.out.println("a candy rolling out!");
}
}
public int getCount() {
return count;
}
public void printstate() {
state.printstate();
}
public State getstate() {
return state;
}
}
此類的設計沿用狀態模式的設計方法。
客戶端類:
public class RemoteMainTest {
public static void main(String[] args) {
try {
CandyMachine service = new CandyMachine("test1", 7);
// LocateRegistry.createRegistry(6602);
Naming.rebind("rmi://127.0.0.1:6602/test1", service);
service.insertCoin();
service = new CandyMachine("test2", 5);
Naming.rebind("rmi://127.0.0.1:6602/test2", service);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.toString());
}
}
}
代理接口:
import java.rmi.Remote;
import java.rmi.RemoteException;
import com.java.jikexueyuan.agentmode.candymachine.State;
public interface CandyMachineRemote extends Remote{
public String getLocation() throws RemoteException;
public int getCount() throws RemoteException;
public State getstate() throws RemoteException;
}
public class Monitor {
private ArrayList<CandyMachineRemote> candyMachinelst;
public Monitor() {
candyMachinelst = new ArrayList<CandyMachineRemote>();
}
public void addMachine(CandyMachineRemote mCandyMachine) {
candyMachinelst.add(mCandyMachine);
}
public void report() {
CandyMachineRemote mCandyMachine;
for (int i = 0, len = candyMachinelst.size(); i < len; i++) {
mCandyMachine = candyMachinelst.get(i);
try {
System.out
.println("Machine Loc:" + mCandyMachine.getLocation());
System.out.println("Machine Candy count:"
+ mCandyMachine.getCount());
System.out.println("Machine State:"
+ mCandyMachine.getstate().getstatename());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
測試類:
public class MainTest {
public static void main(String[] args) {
Monitor mMonitor = new Monitor();
try {
CandyMachineRemote mCandyMachine = (CandyMachineRemote) Naming
.lookup("rmi://127.0.0.1:6602/test1");
mMonitor.addMachine(mCandyMachine);
mCandyMachine = (CandyMachineRemote) Naming
.lookup("rmi://127.0.0.1:6602/test2");
mMonitor.addMachine(mCandyMachine);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mMonitor.report();
}
}
使用場景
1、 遠程代理,也就是爲一個對象在不同的地址空間提供局部代表,這樣可以隱藏一個對象存在於不同地址空間的事實。
2、虛擬代理,是根據需要創建開銷很大的對象,通過它來存放實例化需要很長時間的真實對象。這樣就可以達到性能的最優化,比如圖片對象。
3、安全代理,控制真實對象訪問時的權限。一般用於對象應該有不同的訪問權限的時候。
4、指針引用,是指當調用真實的對象時,代理處理另外一些事。比如計算真實對象的引用次數,這樣當該對象沒有引用時,可以自動釋放它,或當第一次引用一個持久對象時,將它裝入內存,或是在訪問一個實際對象前,檢查是否已經釋放它,以確保其他對象不能改變它。這些都是通過代理在訪問一個對象時附加一些內務處理。
5、延遲加載,用代理模式實現延遲加載的一個經典應用就在 Hibernate 框架裏面。當 Hibernate 加載實體 bean 時,並不會一次性將數據庫所有的數據都裝載。。
優缺點
主要優點有:
1、代理對象作爲客戶端和目標對象之間的中介,起到了保護目標對象的作用。
缺點主要有:
1、由於在客戶端和真實主題之間增加了代理對象,會造成請求的處理速度變慢;
2、實現代理模式需要額外的工作(有些代理模式的實現非常複雜),從而增加了系統實現的複雜度。