Java設計模式-代理模式,java設計模式代理
代理設計模式
代理設計模式:為其他對象提供一種代理以控制對這個對象的訪問。
所謂代理,就是一個人或者機構代表另一個人或者機構採取行動。在一些情況下,一個客戶不想或者不能夠直接引用一個對象,而代理對象可以在用戶端和目標對象之間起到中介的作用。
代理設計模式結構類圖:
代理模式中類或介面的作用:
- 抽象主題角色(Subject):可以使抽象類別也可以是介面,是一個最普通的業務類型定義。聲明了具體主題角色和代理主題角色的共同介面。
- 具體主題角色(RealSubject):也叫作被委託角色、被代理角色。它是冤大頭,是商務邏輯的實際執行者。
- 代理主題角色(Proxy):也叫作委託類、代理類。它負責對真實主題的應用,代理主題角色內部含有具體主題角色的引用,從而可以在任何時候操作目標對象;代理主題角色提供一個與具體主題角色相同的介面,以便可以在任何時候替代具體主題角色。代理主題角色通常會在用戶端調用傳遞給具體主題角色之前或之後,執行一些其他的操作,而不是單純地將調用傳遞給目標對象。
什麼時候使用代理模式?當我們不想直接操作目標類或者想對目標類的方法訪問做一些額外的操作時可以考慮使用代理模式。
代碼模型如下:
// 抽象主題類:定義一個request方法interface Subject{ public void request();}// 真實主題類:正常的業務實作類別,實現了Subject介面class RealSubject implements Subject{ public void request() { System.out.println("RealSubject request"); }}class Proxy implements Subject{ private Subject subject; public Proxy(Subject subject) { this.subject = subject; } @Override public void request() { doBefore(); subject.request(); doAfter(); } // 預先處理 private void doBefore(){ System.out.println("RealSubject request before"); } // 善後處理 private void doAfter(){ System.out.println("RealSubject request after"); }}
這裡面的doBefore和doAfter方法,是在調用方法時做的一些預先處理和善後處理的工作。
代理的實現:
以玩遊戲為例,比如我們要玩英雄聯盟(LOL)打排位,前提是要達到30級,於是我們不想自己去打,就可以找代練來玩(代練這個行業利潤非常可觀)。具體的使用代碼來描述為:
interface IGamePlayer{ void playGame();}class GamePlayer implements IGamePlayer{ private String name; public GamePlayer(String name) { this.name = name; } public void playGame() { System.out.println(name + "正在玩"); }}class GamePlayerProxy implements IGamePlayer{ private IGamePlayer gamePlayer; public GamePlayerProxy(IGamePlayer gamePlayer) { this.gamePlayer = gamePlayer; } @Override public void playGame() { gamePlayer.playGame(); }}public class Test { public static void main(String[] args) { IGamePlayer gamePlayer = new GamePlayer("張三"); GamePlayerProxy gamePlayerProxy = new GamePlayerProxy(gamePlayer); gamePlayerProxy.playGame(); }}
沒有直接使用真實角色GamePlayer的執行個體照樣達到了玩遊戲的效果,是的,有人在給你代練。這個代練的角色就是GamePlayerProxy 類。
普通代理
在上面例子中,我們new了一個真是的角色GamePlayer然後將其傳給代理類GamePlayerProxy,這樣就不需要讓真實的角色來幹活了,所做的操作都由代理來做。普通代理要求用戶端只能訪問代理角色,而不能訪問真是的角色。
/** * * 普通代理:我們要知道代理的存在然後才能訪問 * 要求用戶端只能訪問代理角色,而不能訪問真實角色 * @author qhyuan1992 * */interface IGamePlayer{ void playGame();}class GamePlayer implements IGamePlayer{ private String name; public GamePlayer(String name) { this.name = name; } public void playGame() { System.out.println(name + "正在玩"); }}class GamePlayerProxy implements IGamePlayer{ private IGamePlayer gamePlayer; public GamePlayerProxy(String name) { gamePlayer = new GamePlayer("張三"); } @Override public void playGame() { gamePlayer.playGame(); }}public class Test { public static void main(String[] args) { GamePlayerProxy gamePlayerProxy = new GamePlayerProxy("張三"); gamePlayerProxy.playGame(); }}
所做的改變就是在代理的建構函式中指定要代理的類。這樣代碼更加簡潔,對調用者來說,只需要知道代理的存在而不需要知道代理誰。屏蔽了真實主題角色的變更對上層代碼的影響,真實主題角色想怎麼改變就怎麼改變對上層不會有任何影響。
強制代理
一般是通過代理找到真實的角色,但是強制代理卻是要強制,必須通過真實的角色尋找到代理角色,否則你不能訪問他的方法:不管是通過代理類還是通過真實角色。
/** * 強制代理:必須通過真是的角色尋找到代理角色,否則你不能訪問。 * 不管是通過代理類還是通過真實角色都無法訪問 * @author qhyuan1992 * */interface IGamePlayer{ void playGame(); public IGamePlayer getProxy();}class GamePlayer implements IGamePlayer{ private String name; private IGamePlayer proxy; public GamePlayer(String name) { this.name = name; } public void playGame() { if (isProxy()) { System.out.println(name + "正在玩"); }else { System.out.println("請使用指定的代理訪問"); } } public IGamePlayer getProxy() { proxy = new GamePlayerProxy(this); return proxy; } private boolean isProxy(){ return this.proxy != null; }}class GamePlayerProxy implements IGamePlayer{ private IGamePlayer gamePlayer; public GamePlayerProxy(IGamePlayer gamePlayer) { this.gamePlayer = gamePlayer; } @Override public void playGame() { gamePlayer.playGame(); } @Override public IGamePlayer getProxy() { return this; }}public class Test { public static void main(String[] args) { // 1. 無法訪問:使用真實角色 // 他要求你必須用代理來訪問,你使用真實角色直接來訪問肯定無法訪問 IGamePlayer gamePlayer = new GamePlayer("張三"); gamePlayer.playGame(); // 2. 無法訪問:使用代理類 // 自己new出來的代理,而不是他指定的代理 IGamePlayer gamePlayer = new GamePlayer("張三"); GamePlayerProxy gamePlayerProxy = new GamePlayerProxy(gamePlayer); gamePlayerProxy.playGame(); // 3. 可以訪問:使用真實角色返回的代理 IGamePlayer gamePlayer = new GamePlayer("張三"); GamePlayerProxy proxy = (GamePlayerProxy) gamePlayer.getProxy(); proxy.playGame(); }}
動態代理
在前面我們自己寫的代理的方式就是靜態代理。
靜態代理存在的問題:試想如果Subject介面發生變化。那麼代理類和具體的目標實現都要變化,不是很靈活。
動態代理指的是現階段不用關心代理誰,而在運行階段才指定代理哪一個對象。
而使用Java內建的對代理模式支援的功能來實現則沒有問題。
JDK1.3提供了對動態代理的支援,InvocationHandler介面是JDK提供的動態代理介面,對被代理類的方法進行代理。
動態代理的好處:
- 動態代理跟靜態代理相比,明顯的變化是:靜態代理實現的時候,在Subject介面上定義很多方法,代理類裡面自然要實現很多方法;而動態代理實現的時候雖然Subject定義了很多方法,但是動態代理類始終只有一個invoke方法,這樣,當Subject介面發生變化時。動態代理的介面就不需要跟著變化。
- 動態代理可以更方便的對請求進行任何處理。
/** * 動態代理:是現階段不用關心代理誰,而在運行階段才指定代理哪一個對象。 * 自己寫代理的方式就是靜態代理 * @author qhyuan1992 */interface IGamePlayer{ void playGame();}class GamePlayer implements IGamePlayer{ private String name; public GamePlayer(String name) { this.name = name; } public void playGame() { System.out.println(name + "正在玩"); }}class GamePlayIH implements InvocationHandler{ // 被代理執行個體 Object obj; // 我要代理誰 public GamePlayIH(Object obj) { this.obj = obj; } // 調用被代理的方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { doBefore(); Object result = method.invoke(obj, args); doAfter(); return result; } private void doAfter() { System.out.println("調用後"); } private void doBefore() { System.out.println("調用前"); }}public class Test { public static void main(String[] args) { IGamePlayer player = new GamePlayer("張三"); InvocationHandler handler = new GamePlayIH(player); IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(player.getClass().getClassLoader(), new Class[]{IGamePlayer.class}, handler); proxy.playGame(); }}
輸出:
調用前
張三正在玩
調用後
參考:設計模式之禪/JAVA與模式