频道栏目
首页 > 程序开发 > 软件开发 > 其他 > 正文
设计模式之禅——代理模式(一)普通代理&强制代理&虚拟代理&动态代理
2016-08-29 09:20:30         来源:TyroneRenekton的博客  
收藏   我要投稿

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。

例子:玩家玩网游打怪、升级~

见UML图
这里写图片描述

见代码

//玩家接口类
public interface IGamePlayer {
    public void login(String user, String password);
    public void killBoss();
    public void upgrade();
}
//玩家实现类
public class GamePlayer implements IGamePlayer {
    private String name = "";
    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("张三");
        System.out.println("开始时间是2016-8-25 9:57");
        player.login("张三", "password");
        player.killBoss();
        player.upgrade();
        System.out.println("结束时间是:2016-8-25 9:58");
    }
}
/*Output
开始时间是2016-8-25 9:57
登录名为张三的用户张三登陆成功
张三 在打怪
张三 又生了一级
结束时间是:2016-8-25 9:58
*/

玩过网游的人都知道,升级打怪很累的,一般的网游都是满级之后才开始真正入门(好像暴露了什么~),所以就有了代打。我替你升级,你给我money~

见UML图

这里写图片描述

见代码

//新增代理类
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();

    }

}
//新场景类
public class Client {

    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("张三");
        IGamePlayer proxy = new GamePlayerProxy(player);
        System.out.println("开始时间是:2016-8-25 10:14");
        proxy.login("张三", "password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是: 2016-8-25 10:15");
    }
}

一个主题类和一个代理类,这就是最简洁的代理模式。

它虽然简单但好多设计模式的本质其实就是代理模式(我觉得它就跟继承差不多了,别的模式不涉及到它才怪~)

这里就不说代理模式的优点了~

理模式的拓展

一、普通代理

要求:客户端(场景类只能访问代理角色,而不能访问真实角色)

见UMl图
这里写图片描述

见代码

//修改后的玩家类
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 killBoss() {
        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){
            e.printStackTrace();
        }
    }
    @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();

    }

}
//场景类
public class Client {

    public static void main(String[] args) {
        IGamePlayer proxy = new GamePlayerProxy("张三");
        System.out.println("开始时间是:2016-8-25 10:14");
        proxy.login("张三", "password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是: 2016-8-25 10:15");
    }
}
/*Output
开始时间是2016-8-25 9:57
登录名为张三的用户张三登陆成功
张三 在打怪
张三 又生了一级
结束时间是:2016-8-25 9:58
*/

运行结果完全相同。但是!场景类只知道代理而不知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的玩家(主题角色)想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口对应的方法。
该模式非常适合对拓展性要求非常高的场合。当然,在实际项目中一般都是通过约定来禁止new一个真实的角色,这也是很好的方案。

//书中作者原话
注意 如果团队当中约定应该通过普通代理来完成任务。应该尽量通过团队内的编程规范类约束,因为每一个主题类(玩家)都是可被重用的和可维护的,使用技术约束的方式对系统维护室一种非常不利的因素
//写到这里的我真的是 感觉身体被掏空~ 不过后面还有 强制代理 虚拟代理 动态代理 mdzz啊

二、强制代理(例子不太好)

要求:强制代理不是要求代理替自己干活,而是通过代理来访问我。然后我自己干活~
(其实这个例子的意思是:你去看一个明星,那必须通过他的经纪人。你通过这个经纪人找到这个明星后肯定是和明星交流,而不是和经纪人交流)

UML图看不出来修改了什么地方还占地儿~我们直接来看代码好不好~

的,见代码

//修改后的玩家接口(增加了getProxy()接口)
public interface IGamePlayer {
    public void login(String user, String password);
    public void killBoss();
    public void upgrade();
    //每个人通过getProxy来找到自己的代理
    public IGamePlayer getProxy();
}
//修改后的玩家类
public class GamePlayer implements IGamePlayer {
    private String name = "";
    //我的代理是谁
    private IGamePlayer proxy = null;
    public GamePlayer(String _name){
        this.name = name;
    }
    //找到自己的代理
    @Override
    public IGamePlayer getProxy() {
        // TODO Auto-generated method stub
        this.proxy = new GamePlayerProxy(this.name);
        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 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("请使用指定代理访问");
        }

    }
    private boolean isProxy(){
        if(this.proxy == null) return false;
        else return true;
    }
}
//修改后的代理类
public class GamePlayerProxy implements IGamePlayer {

    private IGamePlayer gamePlayer = null;
    private String name;
    //通过构造函数传递参数确定对谁进行代练
    public GamePlayerProxy(String name){
        this.name = name;
    }
    @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;
    }

}

ok,现在有三个场景类
1、直接访问真实角色

public class Client {

    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("张三");

        System.out.println("开始时间是:2016-8-25 10:14");
        player.login("张三", "password");
        player.killBoss();
        player.upgrade();
        System.out.println("结束时间是: 2016-8-25 10:15");
    }
}
/*Output
开始时间是:2016-8-25 10:14
请使用指定代理访问
请使用指定代理访问
请使用指定代理访问
结束时间是: 2016-8-25 10:15
*/

2、直接访问代理类

public class Client {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("张三");
        IGamePlayer proxy = new GamePlayerProxy(player, ((GamePlayer) player).getName());
        System.out.println("开始时间是:2016-8-25 10:14");
        proxy.login("张三", "password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是: 2016-8-25 10:15");
    }
}
/*Output
开始时间是:2016-8-25 10:14
请使用指定代理访问
请使用指定代理访问
请使用指定代理访问
结束时间是: 2016-8-25 10:15
*/

3、强制代理

public class Client {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("张三");
        IGamePlayer proxy = player.getProxy();
        System.out.println("开始时间是:2016-8-25 10:14");
        proxy.login("张三", "password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是: 2016-8-25 10:15");
    }
}
/*Output
开始时间是:2016-8-25 10:14
登录名为张三的用户登陆成功
 在打怪
 又生了一级
结束时间是: 2016-8-25 10:15
*/

优点就是:不允许你直接访问真实角色

三、虚拟代理
这个其实就是在需要的时候初始化对象,避免被代理对象较多而引起的初始化缓慢而修改了一下代理类初始化的代码

public class Proxy implements Subject{
//要代理的实现类
    private Subject subject;
    //实现接口中定义的方法
    public void requst(){
        if(subject == null){
            subject = new RealSubject();
        }else{
            subject.request();
        }
    }
}

四、动态代理
嗯,上面所有的代理模式都是引子,这个才是重头戏这里写图片描述vcq9vs3Kx76yzKy0+sDtoaPP1tTa09DSu7j2t8ezo8H30NC1xMP7s8a90Nf2w+bP8rrhx9DD5rHgs8yjrNKyvs3Kx0FPUKOoQXNwZWN0IE9yaWVudGVkIFByb2dyYW1taW5no6mjrMbkusvQxL7NyseyydPDwcu2r8ystPrA7bv61saho7u5ysfS1LTy087Pt86qwP08L3A+DQo8cD68+1VNTM28PC9wPg0KPHA+PGltZyBhbHQ9"这里写图片描述" src="/uploadfile/Collfiles/20160829/20160829092512123.jpg" title="\" />

这里的InvocationHandler是JDK提供的动态代理接口

见程序代码

//JDK的接口
package java.lang.reflect;
public interface InvocationHandler {
     public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;
}
//动态代理类

//动态代理就是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称"我已经实现该接口的所有方法了"。
public class GamePlayIH implements InvocationHandler {

    //被代理者
    Class cls = null;
    //被代理的实例
    Object obj = null;
    public GamePlayIH(Object obj){
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        Object result = method.invoke(this.obj, args);
        return result;
    }
}
//最开始的玩家类
public class GamePlayer implements IGamePlayer {
    private String name = "";
    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("张三");
        InvocationHandler handler = new GamePlayIH(player);
        System.out.println("开始时间: 2016-8-28 10:12");
        ClassLoader cl = player.getClass().getClassLoader();
        IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(cl, 
                new Class[]{IGamePlayer.class}, handler);
        proxy.login("zhangsan", "password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间: 2016-8-28 10:14");
    }

}

/*Output
开始时间: 2016-8-28 10:12
登录名为zhangsan的用户张三登陆成功
张三 在打怪
张三 又生了一级
结束时间: 2016-8-28 10:14

*/

没有创建代理类,也没有实现IGamePlayer接口,只需要一个GamePlayH类就可以代理任何需要被代理的类了,这就是动态代理。

AOP是如何依赖动态代理的内容在下一章讲

点击复制链接 与好友分享!回本站首页
上一篇:通用框架功能有哪些
下一篇:【HeadFirst设计模式】(二)观察者模式
相关文章
图文推荐
文章
推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站