ProxyPattern代理模式

代理模式

1.定義

爲其它對象提供一種代理,來控制對這個對象的訪問java

代理模式也叫做委託模式,它能夠提供很是好的訪問控制。代理模式包含三種角色:編程

  • Subject抽象主題角色:能夠是抽象類也能夠接口,定義最普通的業務類型
  • RealSubject具體主題角色:被代理類,被委託類,是業務邏輯的具體執行者
  • Proxy代理主題角色:代理類,委託類。負責對真實角色的應用,把抽象角色內定義的方法限制委託給真實主題角色實現,並在真實主題角色處理完畢先後作預處理及善後處理工做

使用代理模式簡單模擬用戶打遊戲過程,並將其系統化數組

IGamePlayer接口(抽象主題角色):遊戲玩家接口,能夠登陸,打怪,升級框架

public interface IGamePlayer {

    /**
     * 用戶登陸
     * @param username
     * @param password
     */
    void login(String username, String password);

    /**
     * 玩家打怪
     */
    void killBoss();

    /**
     * 玩家升級
     */
    void upgrade();
}

GamePlayer具體類(RealSubject具體主題角色):實現IGamePlayer接口並實現玩家操做ide

public class GamePlayer implements IGamePlayer {

    private String username;

    public GamePlayer(String username) {
        this.username = username;
    }

    public void login(String username, String password) {
        System.out.println("登陸帳號爲" + username + "的用戶" + this.username + "登陸成功!");
    }

    public void killBoss() {
        System.out.println("用戶" + this.username + "在打怪!");
    }

    public void upgrade() {
        System.out.println("用戶" + this.username + "升級!");
    }
}

Client場景類:模擬遊戲玩家的遊戲過程函數

public class Client {

    public static void main(String[] args) {
        // 定義一個遊戲玩家
        IGamePlayer gamePlayer = new GamePlayer("張三");

        // 玩家登陸
        gamePlayer.login("zhangsan", "123456");

        // 玩家打怪
        gamePlayer.killBoss();

        // 玩家升級
        gamePlayer.upgrade();
    }
}

常常打遊戲,熬夜會得網癮,身體頂不住。因而請遊戲代練,將本身的遊戲帳號交給代練,由代練代替玩家打怪升級。一個類能夠實現多個接口,完成不一樣任務的耦合。所以,代理類不單單能夠實習主題接口,也能夠實現其它接口完成不一樣的任務。代理的目的是在真實角色對象方法的基礎上作加強即對真實角色對象方法進行攔截和過濾。this

GamePlayerProxy類(Proxy代理主題角色):遊戲代練,不能做弊,手動打怪升級。要求一樣實現IGamePlayer接口代理

public interface IProxy {

    void before();

    void after();
}

public class GamePlayerProxy implements IGamePlayer, IProxy {

    private IGamePlayer gamePlayer;

    /**
     * 經過構造函數指定被代理的遊戲玩家
     * @param gamePlayer
     */
    public GamePlayerProxy(IGamePlayer gamePlayer) {
        this.gamePlayer = gamePlayer;
    }

    public void login(String username, String password) {
        // 代練登陸
        gamePlayer.login(username, password);
    }

    public void killBoss() {
        this.before();

        // 代練打怪
        gamePlayer.killBoss();
    }

    public void upgrade() {
        // 代練升級
        gamePlayer.upgrade();

        this.after();
    }

    public void before() {
        // 代練打遊戲以前先吃飯補充
        System.out.println("代練吃飯");
    }

    public void after() {
        // 代練打完遊戲以後睡覺休息
        System.out.println("代練睡覺");
    }
}

Client場景類:模擬遊戲代理玩家替代遊戲玩家的遊戲過程code

public class Client {

    public static void main(String[] args) {
        // 定義一個遊戲玩家
        IGamePlayer gamePlayer = new GamePlayer("張三");

        // 定義一個代理玩家
        IGamePlayer gamePlayerProxy = new GamePlayerProxy(gamePlayer);

        // 代理玩家登陸
        gamePlayerProxy.login("zhangsan", "123456");

        // 代理玩家打怪
        gamePlayerProxy.killBoss();

        // 代理玩家升級
        gamePlayerProxy.upgrade();
    }
}

2.應用

2.1 優勢

  • 職責清晰:真實的角色實現實際的業務邏輯,不用關心其它非本職的事務,經過代理完成一件事務
  • 高擴展性:被代理角色能夠隨時發生變化,只須要實現代理接口,代理類不須要作任何修改便可使用
  • 智能化:動態代理模式

2.2 使用場景

只須要關注真實角色的實際業務邏輯,不須要關注預處理及善後工做,減輕被代理類的負擔。Spring AOP是一個典型的動態代理模式示例。對象

3.擴展

3.1 普通代理模式

要求客戶端只能訪問代理角色,而不能訪問真實角色(被代理角色)

以以上游戲過程爲例,場景類不能new一個GamePlayer對象,被代理對象必須由GamePlayerProxy進行模擬場景

普通代理模式的GamePlayer具體類:經過構造函數限制建立真實角色,並傳遞遊戲玩家姓名

public class GamePlayer implements IGamePlayer {

    private String username;

    public GamePlayer(IGamePlayer gamePlayer, String username) {
        if (gamePlayer == null) {
            System.out.println("不能建立真實角色");
        } else {
            this.username = username;
        }
    }

    public void login(String username, String password) {
        System.out.println("登陸帳號爲" + username + "的用戶" + this.username + "登陸成功!");
    }

    public void killBoss() {
        System.out.println("用戶" + this.username + "在打怪!");
    }

    public void upgrade() {
        System.out.println("用戶" + this.username + "升級!");
    }
}

普通代理模式的GamePlayerProxy類

public class GamePlayerProxy implements IGamePlayer, IProxy {

    private IGamePlayer gamePlayer;

    public GamePlayerProxy(String username) {
        try {
            this.gamePlayer = new GamePlayer(this, username);
        } catch (Exception exception) {
            // TODO 異常處理
        }
    }

    public void login(String username, String password) {
        // 代練登陸
        gamePlayer.login(username, password);
    }

    public void killBoss() {
        this.before();

        // 代練打怪
        gamePlayer.killBoss();
    }

    public void upgrade() {
        // 代練升級
        gamePlayer.upgrade();

        this.after();
    }

    public void before() {
        // 代練打遊戲以前先吃飯補充
        System.out.println("代練吃飯");
    }

    public void after() {
        // 代練打完遊戲以後睡覺休息
        System.out.println("代練睡覺");
    }
}

普通代理模式的Client場景類

public class Client {

    public static void main(String[] args) {

        // 定義一個代理玩家
        IGamePlayer gamePlayerProxy = new GamePlayerProxy("張三");

        // 代理玩家登陸
        gamePlayerProxy.login("zhangsan", "123456");

        // 代理玩家打怪
        gamePlayerProxy.killBoss();

        // 代理玩家升級
        gamePlayerProxy.upgrade();
    }
}

普通代理模式調用者只須要知道被代理角色存在,並不須要知道被代理角色的是誰。屏蔽了真實角色的變動對高層模塊的影響。

3.2 強制代理模式

強制代理模式是調用者直接調用真實角色,不用關心代理是否存在,其代理的產生由真實角色決定。

強制代理模式必須經過真實角色找到代理角色,不容許直接訪問真實角色。高層模塊只須要找到真實角色指定的代理角色,就能夠訪問真實角色的全部實際邏輯,代理角色的管理由真實角色本身完成。

強制代理模式IGamePlayer接口

public interface IGamePlayer {

    /**
     * 用戶登陸
     * @param username
     * @param password
     */
    void login(String username, String password);

    /**
     * 玩家打怪
     */
    void killBoss();

    /**
     * 玩家升級
     */
    void upgrade();

    /**
     * 每一個遊戲玩家均可以找本身的代練
     * @return
     */
    IGamePlayer getProxy();
}

強制代理模式GamePlayer具體類

public class GamePlayer implements IGamePlayer {

    private String username;

    private IGamePlayer proxy;

    public GamePlayer(String username) {
        this.username = username;
    }

    public void login(String username, String password) {
        if (this.isProxy()) {
            System.out.println("登陸帳號爲" + username + "的用戶" + this.username + "登陸成功!");
        } else {
            System.out.println("請指定遊戲代練");
        }
    }

    public void killBoss() {
        if (this.isProxy()) {
            System.out.println("用戶" + this.username + "在打怪!");
        } else {
            System.out.println("請指定遊戲代練");
        }
    }

    public void upgrade() {
        if (this.isProxy()) {
            System.out.println("用戶" + this.username + "升級!");
        } else {
            System.out.println("請指定遊戲代練");
        }
    }

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

    /**
     * 檢查是否制定遊戲代練
     * @return
     */
    private boolean isProxy() {
        return this.proxy != null;
    }
}

強制代理模式GamePlayerProxy類

public class GamePlayerProxy implements IGamePlayer, IProxy {

    private IGamePlayer gamePlayer;

    public GamePlayerProxy(IGamePlayer gamePlayer) {
        this.gamePlayer = gamePlayer;
    }

    public void login(String username, String password) {
        // 代練登陸
        gamePlayer.login(username, password);
    }

    public void killBoss() {
        this.before();

        // 代練打怪
        gamePlayer.killBoss();
    }

    public void upgrade() {
        // 代練升級
        gamePlayer.upgrade();

        this.after();
    }

    /**
     * 暫時沒有遊戲代理,就是本身
     * @return
     */
    public IGamePlayer getProxy() {
        return this;
    }

    public void before() {
        // 代練打遊戲以前先吃飯補充
        System.out.println("代練吃飯");
    }

    public void after() {
        // 代練打完遊戲以後睡覺休息
        System.out.println("代練睡覺");
    }
}

強制代理模式Client場景類

public class Client {

    public static void main(String[] args) {

        // 定義一個遊戲玩家
        IGamePlayer gamePlayer = new GamePlayer("張三");

        // 指定遊戲代練
        IGamePlayer proxy = gamePlayer.getProxy();

        // 代理玩家登陸
        proxy.login("zhangsan", "123456");

        // 代理玩家打怪
        proxy.killBoss();

        // 代理玩家升級
        proxy.upgrade();
    }
}

3.3 動態代理

動態代理模式是指在實現階段不用關心代理誰,而在運行階段指定被代理的對象。

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

抽象主題

public interface Subject {

    void doSomething();
}

真實主題:被代理類

public class RealSubject implements Subject {

    @Override
    public void doSomething() {
        System.out.println("========> do something");
        // TODO 業務操做
    }
}

通知接口及其實現

public interface IAdvice {

    /**
     * 執行通知
     */
    void exec();
}

public class BeforeAdvice implements IAdvice {

    /**
     * 前置通知
     */
    @Override
    public void exec() {
        System.out.println("執行前置通知");
        // TODO 執行前置通知業務邏輯
    }
}

public class AfterAdvice implements IAdvice {

    /**
     * 後置通知
     */
    @Override
    public void exec() {
        System.out.println("執行後置通知");
        // TODO 執行後置通知業務邏輯
    }
}

動態代理Handler類:全部動態代理的方法所有經過invoke方法調用

public class MyInvocationHandler implements InvocationHandler {

    /**
     * 被代理對象
     */
    private Object object;

    /**
     * 我要代理的對象
     * @param object
     */
    public MyInvocationHandler(Object object) {
        this.object = object;
    }

    /**
     * 調用被代理的方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 尋找JoinPoint鏈接點
        if (true) {
            // 執行前置通知
            new BeforeAdvice().exec();
        }

        Object result = method.invoke(this.object, args);

        // 尋找JoinPoint鏈接點
        if (true) {
            // 執行後置通知
            new AfterAdvice().exec();
        }

        return result;
    }
}

動態代理類

public class DynamicProxy<T> {

    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] clzss, InvocationHandler handler) {
        // 執行目標,獲取主題代理
        return (T) Proxy.newProxyInstance(loader, clzss, handler);
    }
}

動態代理場景類

public class Client {

    public static void main(String[] args) {

        // 定義一個主題
        Subject subject = new RealSubject();

        // 定義一個Handler
        InvocationHandler handler = new MyInvocationHandler(subject);

        // 定義一個主題代理
        Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);

        // 代理行爲
        proxy.doSomething();
    }
}

subject.getClass().getInterfaces():查找到該類全部的接口,並實現全部的方法。方法的具體實現由new MyInvocationHandler(subject)接管。

動態代理類由InvocationHandler的實現類實現全部的方法,並由其invole方法接管全部方法的實現。

擴展具體業務的動態代理類

public class SubjectDynamicProxy extends DynamicProxy<Subject> {

    public static Subject newProxyInstance(Subject subject) {
        // 獲取ClassLoader
        ClassLoader classLoader = subject.getClass().getClassLoader();

        // 獲取接口數組
        Class<?>[] clzss = subject.getClass().getInterfaces();

        // 獲取Handler
        InvocationHandler handler = new MyInvocationHandler(subject);

        return newProxyInstance(classLoader, clzss, handler);
    }
}

場景類

public class Client {

    public static void main(String[] args) {

        // 定義一個主題
        Subject subject = new RealSubject();

        // 定義一個主題代理
        Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);

        // 代理行爲
        proxy.doSomething();
    }
}

動態代理的意圖就是橫切面編程,在不改變已有代碼結構的狀況下加強或控制對象的行爲。動態代理是一個通用代理框架,實現自定義AOP框架,能夠在此基礎上進行擴展。

相關文章
相關標籤/搜索