再说设计模式-代理模式的扩展

2019-02-21  本文已影响11人  oneape15

引言

我有一篇文章再说设计模式-代理模式,已经讲解了关于代理模式的基本概念,这里我们再来讨论一下代理模式的一些扩展知识。

在网络上代理服务设置分为透明代理和普通代理。透明代理就是用户不用关心设置代理服务器地址,就可以直接访问,也就是说代理服务器对于用户来说是透明的,不用知道它的存在;普通代理则是需要用户自己设置代理服务器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这样非常优秀的工具,拿来主义即可!

上一篇 下一篇

猜你喜欢

热点阅读