代理模式的定義:爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
代理模式可以適用於很多場景,比如在用戶需要某一項東西的資源或者又不需要該目標其他資源的時候,就可以使用代理來加載。剩下一部分資源的消耗。又比如在同一個業務中面對不同的用戶可能執行的動作不盡相同,這種情況也可以使用代理來調用用戶的各種行爲,就能夠成功實現代理對用戶權限的一個控制。
代理模式又分爲靜態代理和動態代理,靜態代理中,可能根據需要創建很多個RealSubject,而動態代理只需要一個RealSubject就夠了,哪怕你要代理的RealObject是不同的對象,甚至代理不同的方法,都可以動過動態代理,來擴展功能。。動態代理在java的主流框架,如Spring裏用得比較多。
現在建立一個java的代碼實例,來理解一下代理模式。
案例:
在《極品飛車》遊戲裏中,玩家可以購買更高級的賽車,但是必須要達到一定的賽車手級別才能購買。因此買車時,我們需要對玩家進行一個權限控制,達到要求的玩家可以查看賽車信息併購買,而未達標的玩家無法購買高級賽車,只能查看賽車信息。
在這個案例中,商店就相當於是玩家獲得“賽車”的一個靜態代理,由代理實現玩家身份的判斷,來進行不同的行爲。
問題:如何實現權限控制呢,在client中進行嗎?這明顯不符合“開閉原則”,client中應該對用戶友好,不應添加那麼多購物的控制邏輯。如果在遊戲邏輯類中進行判定的話,又會導致耦合度太高,不利於後續的拓展開發(比如遊戲活動,降價打折…),就像MVC模式中分層一樣,我們單獨抽出一層作爲業務邏輯,當然,作爲“業務邏輯”之一的“購買賽車”也需要在這一層編寫。只留一個接口供處在View層的client調用,這個接口就是代理模式提供的接口。
————設計類圖如下————
————設計代碼如下————
先定義了一個買車行爲的接口(單詞拼錯了,buy拼成bug了)
public interface BugCar {
public void buy_my_car();
}
然後有兩個類分別去實現該接口。一個是不論什麼角色的玩家都可以產生行爲的類Player,另一個是達到要求的玩家可以通過代理獲取車輛的Proxy類:
public class Player implements BugCar {
private int gold;
private int level;
private String userName;
@Override
public void buy_my_car() {
System.out.println("賽車信息:保時捷科邁羅2.0T自動RS,最高車速240km/h,手自一體7檔變速");
System.out.println(" 100km/h加速5.9秒,渦輪增壓,缸內直噴。最大功率202");
}
public int getGold() {
return gold;
}
public void setGold(int gold) {
this.gold = gold;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
代理類————
public class BuyCarProxy implements BugCar {
private Player player;
public Player getPlayer(){
return player;
}
public void setPlayer(Player player){
this.player = player;
}
@Override
public void buy_my_car() {
player.buy_my_car();//查看車輛屬性
if (player.getLevel() >10 && player.getGold()>=50000){ //等級金幣符合要求
System.out.println("等級金幣符合要求");
player.setGold(player.getGold()-50000); //扣除金幣
System.out.println(player.getUserName()+",恭喜你獲得新座駕!"); System.out.println("==================================================");
return ;
} else {
System.out.println(player.getUserName()+"條件不足,繼續參加比賽吧!");
}
System.out.println("==================================================");
}
}
最後在客戶端裏(假設已經創建好了玩家和玩家的各種屬性,這裏只考慮主類中對不同玩家相同行爲做出的不同反應,因此在client中創建角色這種高度耦合的代碼不在討論範圍之內)創建好角色,來驗證一下在面對不同情況的用戶,是否能實現這個簡單的權限控制。
public class client {
public static void initGame(){
Player ply_01,ply_02,ply_03;
/**
* 創建3個玩家信息
*/
ply_01 = new Player();
ply_01.setUserName("巨石強森");
ply_01.setLevel(15);
ply_01.setGold(92300);
ply_02 = new Player();
ply_02.setUserName("保羅");
ply_02.setLevel(8);
ply_02.setGold(60391);
ply_03 = new Player();
ply_03.setUserName("範迪塞爾");
ply_03.setLevel(12);
ply_03.setGold(39480);
BuyCarProxy proxy = new BuyCarProxy();//創建一個代理
proxy.setPlayer(ply_01);
proxy.buy_my_car();
proxy.setPlayer(ply_02);
proxy.buy_my_car();
proxy.setPlayer(ply_03);
proxy.buy_my_car();
}
public static void main(String[] args) {
initGame();
}
}
點擊一下運行,到控制檯中,看一下結果:
在這個例子裏,利用java中的多態特性,所有玩家都可以調用Player(RealSubject)裏的buy方法查看賽車的參數信息。但需要購買賽車時,代理(BuyCarProxy)就會重載buy方法,檢驗玩家是否有權限解鎖新賽車。無法購買的話就只調用RealSubject裏的方法查看賽車信息,並提示無法購買。如果可以購買,就在查看賽車信息的基礎上進行額外的購買操作。
該例中我們只完成了購買此行爲的權限控制,同樣的,我們也可以通過這種形式達到對數據庫的訪問權限的控制。在沒有達到權限的時候,是不能是數據庫訪問的。這樣可以更好的保護數據的安全。