ProxyPattern代理模式

Posted jeff-chang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ProxyPattern代理模式相关的知识,希望对你有一定的参考价值。

代理模式

1.定义

为其它对象提供一种代理,来控制对这个对象的访问

代理模式也叫作委托模式,它可以提供非常好的访问控制。代理模式包含三种角色:

  • Subject抽象主题角色:可以是抽象类也可以接口,定义最普通的业务类型
  • RealSubject具体主题角色:被代理类,被委托类,是业务逻辑的具体执行者
  • Proxy代理主题角色:代理类,委托类。负责对真实角色的应用,把抽象角色内定义的方法限制委托给真实主题角色实现,并在真实主题角色处理完毕前后做预处理及善后处理工作

使用代理模式简单模拟用户打游戏过程,并将其系统化

IGamePlayer接口(抽象主题角色):游戏玩家接口,可以登录,打怪,升级

public interface IGamePlayer {

    /**
     * 用户登录
     * @param username
     * @param password
     */
    void login(String username, String password);

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

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

GamePlayer具体类(RealSubject具体主题角色):实现IGamePlayer接口并实现玩家操作

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

经常打游戏,熬夜会得网瘾,身体顶不住。于是请游戏代练,将自己的游戏账号交给代练,由代练代替玩家打怪升级。一个类可以实现多个接口,完成不同任务的耦合。因此,代理类不仅仅可以实习主题接口,也可以实现其它接口完成不同的任务。代理的目的是在真实角色对象方法的基础上做增强即对真实角色对象方法进行拦截和过滤。

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场景类:模拟游戏代理玩家替代游戏玩家的游戏过程

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框架,可以在此基础上进行扩展。

以上是关于ProxyPattern代理模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之:代理模式ProxyPattern的实现

ProxyPattern代理模式

设计模式 - 学习笔记 - 代理模式ProxyPattern

设计模式 - 学习笔记 - 代理模式ProxyPattern

代理模式

大话涉及模式Python实现-代理模式