设计模式之禅——代理模式(一)普通代理&强制代理&虚拟代理&动态代理

来源:互联网 发布:防sql注入攻击 编辑:程序博客网 时间:2024/06/08 07:33

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

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

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

四、动态代理
嗯,上面所有的代理模式都是引子,这个才是重头戏这里写图片描述

动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定带了哪一个。相对来说,自己写代理类的方式就是静态代理。现在有一个非常流行的名称叫做面向横切面编程,也就是AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制。还是以打游戏为例

见UML图

这里写图片描述

这里的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是如何依赖动态代理的内容在下一章讲

6 0
原创粉丝点击