代理模式

代理模式的定義

代理模式(Proxy Pattern)是一個使用率非常高的模式,其定義如下:
Provide a surrogate or placeholder for another object to control access to it.(爲其他對象提供一種代理以控制對這個對象的訪問。)

public interface Subject {
    void request();
}

public class RealSubject implements Subject {
    @Override
    public void request() {
        //業務邏輯處理
    }
}
public class Proxy implements Subject {
    private Subject subject = null;
    public Proxy() {
        this.subject = new Proxy();
    }

    public Proxy(Object... objects) {

    }

    @Override
    public void request() {
        this.before();
        this.subject.request();
        this.after();
    }

    private void before() {
    }
    private void after(){

    }
}

代理模式的應用

優點

  • 職責清晰
  • 高擴展性
  • 智能化

使用場景

…………………………(很多)

代理模式的擴展

普通代理

普通代理要求客戶端只能訪問代理角色,而不能訪問真實角色

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

    //殺怪
    public void kissBoss();

    //升級
    public void upgrade();
}
public class GamePlayer implements IGamePlayer {
    private String name = "";

    public GamePlayer(IGamePlayer _gamePlayer, String _name) throws Exception {
        if (_gamePlayer == null) {
            throw new Exception("不能創建角色");
        }else {
            this.name = _name;
        }
    }
    @Override
    public void login(String user, String password) {
        System.out.println("登錄名爲" + user + "的用戶" + this.name + "登錄成功");
    }

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

    @Override
    public void upgrade() {
        System.out.println(this.name+"又升了一級!");
    }
}
public class GamePlayerProxy implements IGamePlayer {
    private IGamePlayer gamePlayer = null;

    public GamePlayerProxy(String name) {
        try {
            gamePlayer = new GamePlayer(this, name);
        } catch (Exception e) {

        }
    }

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

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

    @Override
    public void upgrade() {
        this.gamePlayer.upgrade();
    }
}
public class Client {
    public static void main(String[] args) {
        IGamePlayer proxy = new GamePlayerProxy("張三");
        System.out.println("開始時間是:2018-09-26 10:00");
        proxy.login("zhangsan", "password");
        proxy.kissBoss();
        proxy.upgrade();
        System.out.println("結束時間是:2018-09-27 10:00");
    }
}

注意 普通代理模式的約束問題,儘量通過團隊內的編程規範類約束,因爲每一個主題類是可被重用的和可維護的,使用技術約束的方式對系統維護是一種非常不利的因素。

強制代理

強制代理卻是要“強制”,你必須通過真實角色查找到代理角色,否則你不能訪問。甭管你是通過代理類還是通過直接new一個主題角色類,都不能訪問,只有通過真實角色指定的代理類纔可以訪問,也就是說由真實角色管理代理角色。

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

    //殺怪
    public void kissBoss();

    //升級
    public void upgrade();

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

public class GamePlayer implements IGamePlayer {
    private String name = "";
    //我的代理是誰
    private IGamePlayer proxy = null;

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

    //找到自己的代理
    @Override
    public IGamePlayer getProxy() {
        this.proxy = new GamePlayerProxy(this);
        return this.proxy;
    }

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

    @Override
    public void kissBoss() {
        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("請使用代理訪問");
        }
    }

    //檢查是否是代理訪問
    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 kissBoss() {
        this.gamePlayer.kissBoss();
    }

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

    //代理的代理暫時還沒有,就是自己
    @Override
    public IGamePlayer getProxy() {
        return this;
    }
}

如果直接訪問真實角色

public class Client {
    public static void main(String[] args) {
        IGamePlayer proxy = new GamePlayer("張三");
        System.out.println("開始時間是:2018-09-26 10:00");
        proxy.login("zhangsan", "password");
        proxy.kissBoss();
        proxy.upgrade();
        System.out.println("結束時間是:2018-09-27 10:00");
    }
}

運行結果就會如下所示

開始時間是:2018-09-26 10:00
請使用代理訪問
請使用代理訪問
請使用代理訪問
結束時間是:2018-09-27 10:00

那麼直接訪問代理呢

public class Client {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("張三");
        IGamePlayer proxy = new GamePlayerProxy(player);
        System.out.println("開始時間是:2018-09-26 10:00");
        proxy.login("zhangsan", "password");
        proxy.kissBoss();
        proxy.upgrade();
        System.out.println("結束時間是:2018-09-27 10:00");
    }
}

結果還是不行

開始時間是:2018-09-26 10:00
請使用代理訪問
請使用代理訪問
請使用代理訪問
結束時間是:2018-09-27 10:00

這是因爲這個代理是你new出來的,不是玩家指定的

public class Client {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("張三");
        IGamePlayer proxy = player.getProxy();
        System.out.println("開始時間是:2018-09-26 10:00");
        proxy.login("zhangsan", "password");
        proxy.kissBoss();
        proxy.upgrade();
        System.out.println("結束時間是:2018-09-27 10:00");
    }
}

這次終於可以了。

代理是有個性的

一個類可以實現多個接口,完成不同任務的整合。也就是說代理類不僅僅可以實現主題接口,也可以實現其他接口完成不同的任務,而且代理的目的是在目標對象方法的基礎上作增強,這種增強的本質通常就是對目標對象的方法進行攔截和過濾。
例如,增加一個IPorxy接口,作用是用來計算代理的費用。

public interface IPorxy {
    //計算費用
    public void count();
}
public class GamePlayerProxy implements IGamePlayer,IPorxy {
    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 kissBoss() {
        this.gamePlayer.kissBoss();
    }

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

    //代理的代理暫時還沒有,就是自己
    @Override
    public IGamePlayer getProxy() {
        return this;
    }

    @Override
    public void count() {
        System.out.println("升級總費用爲 150元");
    }
}

代理類不僅僅是可以有自己的運算方法,通常的情況下代理的職責並不一定單一,它可以組合其他的真實角色,也可以實現自己的職責,比如計算費用。代理類可以爲真實角色預處理消息、過濾消息、消息轉發、事後處理消息等功能。當然一個代理類,可以代理多個真實角色,並且真實角色之間可以有耦合關係

動態代理

動態代理是在實現階段不用關心代理誰,而在運行階段才指定代理哪一個對象。相對來說,自己寫代理類的方式就是靜態代理。本章節的核心部分就在動態代理上,現在有一個非常流行的名稱叫做面向橫切面編程,也就是AOP(Aspect Oriented Programming),其核心就是採用了動態代理機制

public class GamePlayIH implements InvocationHandler {
    //被代理者
    Class cls = null;
    //被代理的實例
    Object object = null;

    //我要代理誰
    public GamePlayIH(Object _obj) {
        this.object = _obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.object, args);
        if (method.getName().equalsIgnoreCase("login")) {
            System.out.println("有人在用我的賬號登錄");
        }
        return result;
    }
}
public class Client {
    public static void main(String[] args) {
        //定義一個癡迷的玩家
        IGamePlayer player = new GamePlayer("張三");
        //定義一個handler
        InvocationHandler handler = new GamePlayIH(player);
        //開始遊戲
        System.out.println("開始時間是:2018-09-26 10:00");
        //獲得類的class loader
        ClassLoader c1 = player.getClass().getClassLoader();
        //動態產生一個代理類
        IGamePlayer proxy = (IGamePlayer) java.lang.reflect.Proxy.newProxyInstance(c1, new Class[]{IGamePlayer.class}, handler);
        proxy.login("zhangsan", "password");
        proxy.kissBoss();
        proxy.upgrade();
        System.out.println("結束時間是:2018-09-27 10:00");
    }
}

我們看一下一個通用的動態代理代碼

public interface Subject {
    //業務操作
    public void doSomething(String string);
}

public class RealSubject implements Subject {
    @Override
    public void doSomething(String string) {
        System.out.println("do something --->" + string);
    }
}
public class MyInvocationHandler implements InvocationHandler {
    //被代理的對象
    private Object target = null;
    //通過構造函數傳遞一個對象

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    //代理方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //執行被代理的方法
        return method.invoke(this.target,args);
    }
}
public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) {
        //尋找JoinPoint連接點,AOP框架使用源數據定義
        if(true){
            //執行一個前置通知
            (new BeforeAdvice()).exce();
        }
        return (T) Proxy.newProxyInstance(loader, interfaces, handler);
    }
}

public interface IAdvice {
    //通知只有一個方法,執行即可
    public void exce();
}

public class BeforeAdvice implements IAdvice {
    @Override
    public void exce() {
        System.out.println("前置通知執行了!");
    }
}

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("Finish");
    }
}

具體業務的動態代理


public class SubjectDynamicProxy extends DynamicProxy {
    public static <T> T newProxyInstance(Subject subject) {
        ClassLoader classLoader = subject.getClass().getClassLoader();
        Class<?>[] classes = subject.getClass().getClasses();
        InvocationHandler handler = new MyInvocationHandler(subject);
        return newProxyInstance(classLoader, classes, handler);
    }
}

場景類更加簡單

public class Client {
    public static void main(String[] args) {
        //定義一個主題
        Subject subject = new RealSubject();
        //定義一個Handler
        InvocationHandler handler = new MyInvocationHandler(subject);
        //定義主題的代理
        Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
        //代理的行爲
        proxy.doSomething("Finish");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章