再說設計模式-代理模式的擴展

引言

我有一篇文章再說設計模式-代理模式,已經講解了關於代理模式的基本概念,這裏我們再來討論一下代理模式的一些擴展知識。

在網絡上代理服務設置分爲透明代理和普通代理。透明代理就是用戶不用關心設置代理服務器地址,就可以直接訪問,也就是說代理服務器對於用戶來說是透明的,不用知道它的存在;普通代理則是需要用戶自己設置代理服務器IP地址,用戶必須知道代理的存在。

而在我們設計模式中的普通代理強制代理也是類似的一種結構。
普通代理 - 就是我們要知道代理的存在;
強制代理 - 調用者直接調用真實角色,而不用關心代理是否存在,其代理的產生是由真實角色決定的。

普通代理

爲了簡單說明,我們以遊戲代練爲例子(在玩遊戲時,我們覺得升級很麻煩,所以找專門的遊戲代練團隊幫助升級)。 我自己做爲一個遊戲玩家,我肯定自己不練級了,也就是場景類(Client)不能直接new一個GramePlayer對象了,它必須由GamePlayerProxy來進行模擬場景,類圖如下所示:


調用者只知道代理而不用知道真實的角色是誰,屏蔽了真實角色的變更對高層模塊的影響,真實的主題角色想怎麼修改就怎麼修改,對高層次的模塊沒有任何的影響,只要你實現了接口所對應的方法,該模式非常適合對擴展性要求的場合。

強制代理

強制代理在設計模式中比較特殊,爲什麼這麼講呢?因爲一般的情況下,通過代理找到真實的角色,但是強制代理卻是要強制,你必須通過真實角色找到代理角色,否則你不能訪問。甭管你是通過代理類還是通過直接new一個主題角色類,都不能訪問,只有通過真實角色指定的代理類纔可以訪問,也就是說由真實角色代理角色。
就好比你和大明星華仔很熟,相互認識,有件事你需要向他確認一下,於是你就直接撥通了華仔的電話:

"喂,華仔呀,我要見一個某某導演,你幫下忙呀!"
"不行呀,猿哥,我這幾天很忙的,你找我的經紀人吧……"

so~~,你想直接繞過他的代理,誰知道返回的還是他的代理,這就是強制代理,你可以不用知道代理存在,但是你的所作所爲還是需要代理爲你提供。其類圖如下所示:


// 遊戲用戶接口類
public interface IGamePlayer {
    // 登錄遊戲
    public void login(String user, String password);

    // 殺怪
    public void killBoss();

    // 升級
    public void upgrade();

    // 每個人都可以找一下自己的代理
    public IGamePlayer getProxy();

}
// 真實類
public class GamePlayer implements IGamePlayer {

    private String      name  = "";
    // 我的代理
    private IGamePlayer proxy = null;

    public GamePlayer(String _name) {
        this.name = _name;
    }

    @Override
    public void login(String user, String password) {
        if (this.isProxy()) {
            System.out.println("登錄名爲【" + user + "】的用戶【" + this.name + "】登錄成功!");
        } else {
            System.out.println("請使用指定代理訪問");
        }
    }

    @Override
    public void killBoss() {
        if (this.isProxy()) {
            System.out.println(this.name + "在打怪!");
        } else {
            System.out.println("請使用指定代理訪問");
        }
    }

    @Override
    public void upgrade() {
        if (this.isProxy()) {
            System.out.println(this.name + " 已升了一級!");
        } else {
            System.out.println("請使用指定代理訪問");
        }
    }

    @Override
    public IGamePlayer getProxy() {
        this.proxy = new GamePlayerProxy(this);
        return this.proxy;
    }

    // 校驗是否是代理訪問
    private boolean isProxy() {
        if (this.proxy == null) {
            return false;
        } else {
            return true;
        }
    }
}
// 代理類
public class GamePlayerProxy implements IGamePlayer {

    private IGamePlayer gamePlayer = null;

    // 構造函數傳遞用戶
    public GamePlayerProxy(IGamePlayer _gamePlayer) {
        this.gamePlayer = _gamePlayer;
    }

    @Override
    public void login(String user, String password) {
        this.gamePlayer.login(user, password);
    }

    @Override
    public void killBoss() {
        this.gamePlayer.killBoss();
    }

    @Override
    public void upgrade() {
        this.gamePlayer.upgrade();
    }

    @Override
    public IGamePlayer getProxy() {
        return this;
    }
}
// 場景類
public class Client {
    public static void main(String[] args) {
        requestOfSubject();
        System.out.println("~~~~~~~~~~~~~~分隔線~~~~~~~~~~~~~~~~");
        requestOfProxy();
        System.out.println("~~~~~~~~~~~~~~分隔線~~~~~~~~~~~~~~~~");
        requestOfForceProxy();
    }

    /**
     * 直接訪問真實角色
     */
    private static void requestOfSubject() {
        IGamePlayer player = new GamePlayer("猿哥");

        System.out.println("開始時間是:" + System.nanoTime());
        player.login("oneape15", "pwd123");
        player.killBoss();
        player.upgrade();
        System.out.println("遊戲結束(直接訪問真實角色)");
    }

    /**
     * 直接訪問代理類
     */
    private static void requestOfProxy() {
        IGamePlayer player = new GamePlayer("猿哥");
        IGamePlayer proxy = new GamePlayerProxy(player);

        System.out.println("開始時間是:" + System.nanoTime());
        proxy.login("oneape15", "pwd123");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("遊戲結束(直接訪問代理類)");
    }

    /**
     * 使用強制代理的場景
     */
    private static void requestOfForceProxy() {
        IGamePlayer player = new GamePlayer("猿哥");
        IGamePlayer proxy = player.getProxy();

        System.out.println("開始時間是:" + System.nanoTime());
        proxy.login("oneape15", "pwd123");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("遊戲結束(使用強制代理的場景)");
    }
}

運行結果如下:


動態代理

動態代理是在實現階段不用關心代理是誰,而在運行階段才指定代理哪一個對象。相對來說,自己寫代理類的方式就是靜態代理。我們大家都知道的一個非常流行的名稱叫面向橫切面編程,也就是AOP(Aspect Oriented Programming),其核心就是採用了動態代理機制。我們這裏還以打遊戲爲例,其類圖,修改如下:

其中InvocationHandler是JDK提供的動態代理接口,對被代理類的方法進行代理。
接口類:

public interface IGamePlayer {
    // 登錄遊戲
    public void login(String user, String password);

    // 殺怪
    public void killBoss();

    // 升級
    public void upgrade();

}

動態代理類:

public class GamePlayIH implements InvocationHandler {
    // 被代理者
    Class  clazz = null;
    // 被代理實例
    Object obj   = null;

    // 我要代理誰
    public GamePlayIH(Object _obj) {
        this.obj = _obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.obj, args);
    }
}

真實角色類:

public class GamePlayer implements IGamePlayer {

    private String      name  = "";
    // 我的代理
    private IGamePlayer proxy = null;

    public GamePlayer(String _name) {
        this.name = _name;
    }

    @Override
    public void login(String user, String password) {
        System.out.println("登錄名爲【" + user + "】的用戶【" + this.name + "】登錄成功!");
    }

    @Override
    public void killBoss() {
        System.out.println(this.name + "在打怪!");
    }

    @Override
    public void upgrade() {
        System.out.println(this.name + " 已升了一級!");
    }
}

場景類:

public class Client {
    public static void main(String[] args) {
        // 定義一個癡迷的玩家
        IGamePlayer player = new GamePlayer("猿哥");
        // 定義一個Handler
        InvocationHandler handler = new GamePlayIH(player);

        // 開始打遊戲,記下時間戳
        System.out.println("開始時間: " + new Date());

        // 獲得類的class loader
        ClassLoader cl = player.getClass().getClassLoader();
        // 動態產生一個代理者
        IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl, new Class[]{IGamePlayer.class}, handler);
        // 登錄
        proxy.login("oneape15", "pwd123");
        //開始殺怪
        proxy.killBoss();
        // 升級
        proxy.upgrade();
        // 結束
        System.out.println("結果遊戲: "+ new Date());
    }
}

運行結果如下:


動態代理是根據被代理的接口生成所有方法,也就是說給定一個接口,動態代理會宣稱我已經實現該接口下的所有方法了

接着上面說的AOP編程,AOP編程沒有使用什麼新的技術,但是它對我們的設計、編碼有非常大的影響,對於日誌、事務、權限都可以在系統設計階段不用考慮,而在設計後通過AOP的方式切過去。通用動態代理模型如下:


最佳實踐

代理模式應用得非常廣泛,大到一個系統框架,企業平臺,小到代碼片段、事務處理,稍不留意就用到代理模式。可能該模式是大家接觸最多的模式,而且有了AOP大家寫代理就更加簡單了,有類似Spring AOP和AspectJ這樣非常優秀的工具,拿來主義即可!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章