[转]《设计模式之禅》样章连载6:代理模式扩展之“普通代理”和“强制代理”

来源:互联网 发布:python multiply 编辑:程序博客网 时间:2024/05/29 23:24
转自:http://student.csdn.net/space.php?uid=259146&do=blog&id=25986

12.4 代理模式的扩展

12.4.1 普通代理

在网络上代理服务器设置分为透明代理和普通代理,是什么意思呢?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说透明的,看不到,不用知道它存在的;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,这样解释还是比较复杂,我们还是用实例来讲解。

首先说普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色,这是比较简单的,我们以上面的例子作为扩展,我自己作为一个游戏玩家,我肯定自己不练级了,也就是场景类不能再直接new一个GamePlayer对象了,它必须由GampePlayerProxy来进行模拟场景,类图修改如图12-4所示。

<!--[if !supportLists]--> 图12-4        <!--[endif]-->普通代理类图

改动很小,仅仅修改了两个实现类的构造函数,GamePlayer的构造函数增加了_gamePlayer参数,而代理角色则只要传入代理者名字即可,而不需要说是替哪个对象做代理。GamePlayer类如代码清单12-10所示。

<!--[if !supportLists]-->代码清单12-1              <!--[endif]-->普通代理的游戏者

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;

            }

     }

     //打怪,最期望的就是杀老怪

     public void killBoss() {

            System.out.println(this.name + "在打怪!");

     }

     //进游戏之前你肯定要登录吧,这是一个必要条件

     public void login(String user, String password) {

            System.out.println("登录名为"+user + " 的用户 " + this.name + "登录成功!");

     }

     //升级,升级有很多方法,花钱买是一种,做任务也是一种

     public void upgrade() {

            System.out.println(this.name + " 又升了一级!");

     }

}

在构造函数中,传递进来一个IGamePlayer对象,检查谁能创建真实的角色,当然还可以有其他的限制,比如类名必须为Proxy类等等,读者可以根据实际情况进行扩展。GamePlayerProxy如代码清单12-11所示。

<!--[if !supportLists]-->代码清单12-2              <!--[endif]-->普通代理的代理者

public class GamePlayerProxy implements IGamePlayer {

     private IGamePlayer gamePlayer = null;       

     //通过构造函数传递要对谁进行代练

     public GamePlayerProxy(String name){

            try {

                    gamePlayer = new GamePlayer(this,name);

            } catch (Exception e) {

                    // TODO 异常处理

            }

     }

     //代练杀怪

     public void killBoss() {

            this.gamePlayer.killBoss();

     }

     //代练登录

     public void login(String user, String password) {

            this.gamePlayer.login(user, password);

     }

     //代练升级

     public void upgrade() {

            this.gamePlayer.upgrade();

     }

}

仅仅修改了构造函数,传递进来一个代理者名称,即可进行代理,在这种改造下,系统更加简洁了,调用者只知道代理存在就可以,不用知道代理了谁。同时场景类也稍作改动,如代码清单12-12所示。

<!--[if !supportLists]-->代码清单12-3              <!--[endif]-->普通代理的场景类

public class Client {

<!--[if !supportEmptyParas]--> <!--[endif]-->

     public static void main(String[] args) {

            //然后再定义一个代练者

            IGamePlayer proxy = new GamePlayerProxy("张三");             

            //开始打游戏,记下时间戳

            System.out.println("开始时间是:2009-8-25 10:45");

            proxy.login("zhangSan", "password");

            //开始杀怪

            proxy.killBoss();

            //升级

            proxy.upgrade();

            //记录结束游戏时间

            System.out.println("结束时间是:2009-8-26 03:40");

     }

}

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

注意  普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。

12.4.2 强制代理

强制代理在设计模式中比较另类,为什么这么说呢?一般的思维都是通过代理找到真实的角色,但是强制代理却是要“强制”,你必须通过真实角色查找到代理角色,否则你不能访问,甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色,这么说吧,高层模块new了一个真实角色的对象,返回的却是代理角色,这就好比是你和一个明星比较熟,相互认识,有件事情你需要向她确认一下,于是你就直接拨通了明星的电话:

“喂,沙比呀,我要见一下XXX导演,你帮下忙了!”

“不行呀衰哥,我这几天很忙呀,你找我的经纪人吧…”

郁闷了吧,你是想直接绕过她的代理,谁知道返回的还是她的代理,这就是强制代理,你可以不用知道代理存在,但是你的所作所为还是需要代理为你提供。我们把上面的例子稍作修改就可以完成,如图12-5所示。

<!--[if !supportLists]-->图12-5        <!--[endif]-->强制代理类图

在接口上增加了一个getProxy方法,真实角色GamePlayer可以指定一个自己的代理,除了代理外谁都不能访问。我们来看代码,先看IGamePlayer接口,如代码清单12-13所示。

<!--[if !supportLists]-->代码清单12-4              <!--[endif]-->强制代理的接口类

public interface IGamePlayer {

     //登录游戏

     public void login(String user,String password);      

     //杀怪,这是网络游戏的主要特色

     public void killBoss();       

     //升级

     public void upgrade();

     //每个人都可以找一下自己的代理

     public IGamePlayer getProxy();

}

仅仅增加了一个getProxy方法,指定要访问自己必须通过哪个代理,实现类也要做适当的修改,先看真实角色GamePlayer,如代码清单12-14所示。

<!--[if !supportLists]-->代码清单12-5              <!--[endif]-->强制代理的真实角色

public class GamePlayer implements IGamePlayer {

      private String name = "";

      //我的代理是谁

      private IGamePlayer proxy = null;

             

      public GamePlayer(String _name){

              this.name = _name;       

      }

      //找到自己的代理

      public IGamePlayer getProxy(){

              this.proxy = new GamePlayerProxy(this.name);

              return this.proxy;

      }

      //打怪,最期望的就是杀老怪

      public void killBoss() {

              if(this.isProxy()){

                       System.out.println(this.name + "在打怪!");

              }else{

                       System.out.println("请使用指定的代理访问");

              }

      }

      //进游戏之前你肯定要登录吧,这是一个必要条件

      public void login(String user, String password) {

              if(this.isProxy()){

                       System.out.println("登录名为"+user + " 的用户 " + this.name + "登录成功!");

              }else{

                       System.out.println("请使用指定的代理访问");;

              }

      }

      //升级,升级有很多方法,花钱买是一种,做任务也是一种

      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;

              }

      }

}

增加了一个私有方法,检查是否是自己指定的代理,是指定的代理则允许访问,否则不允许访问。我们再来看代理角色,如代码清单12-15所示。

<!--[if !supportLists]-->代码清单12-6              <!--[endif]-->强制代理的代理类

public class GamePlayerProxy implements IGamePlayer {

     private IGamePlayer gamePlayer = null;

     //构造函数传递用户名

     public GamePlayerProxy(IGamePlayer _gamePlayer){     

            this.gamePlayer = _gamePlayer;

     }

     //代练杀怪

     public void killBoss() {

            this.gamePlayer.killBoss();

     }

     //代练登录

     public void login(String user, String password) {

            this.gamePlayer.login(user, password);

     }

     //代练升级

     public void upgrade() {

            this.gamePlayer.upgrade();

     }

     //代理的代理暂时还没有,就是自己

     public IGamePlayer getProxy(){

            return this;

     }

}

代理角色也可以再次被代理,这里我们就没有继续延伸下去了,查找代理的方法就返回自己的实例。代码都写完毕了,我们先按照常规的思路来运行一下,直接new一个真实角色,如代码清单12-16所示。

<!--[if !supportLists]-->代码清单12-7              <!--[endif]-->直接访问真角色

public class Client {

<!--[if !supportEmptyParas]--> <!--[endif]-->

     public static void main(String[] args) {

            //定义个游戏的角色

            IGamePlayer player = new GamePlayer("张三");         

            //开始打游戏,记下时间戳

            System.out.println("开始时间是:2009-8-25 10:45");

            player.login("zhangSan", "password");

            //开始杀怪

            player.killBoss();

            //升级

            player.upgrade();

            //记录结束游戏时间

            System.out.println("结束时间是:2009-8-26 03:40");

     }

}

想想看能运行吗?运行结果如下所示:

开始时间是:2009-8-25 10:45

请使用指定的代理访问

请使用指定的代理访问

请使用指定的代理访问

结束时间是:2009-8-26 03:40

它要求你必须通过代理来访问,你想要直接访问它,门儿都没有,好,你要我通过代理来访问,那就生产一个代理,如代码清单12-17所示。

<!--[if !supportLists]-->代码清单12-8              <!--[endif]-->直接访问代理类

public class Client {

<!--[if !supportEmptyParas]--> <!--[endif]-->

     public static void main(String[] args) {

            //定义个游戏的角色

            IGamePlayer player = new GamePlayer("张三");

            //然后再定义一个代练者

            IGamePlayer proxy = new GamePlayerProxy(player);            

            //开始打游戏,记下时间戳

            System.out.println("开始时间是:2009-8-25 10:45");

            proxy.login("zhangSan", "password");

            //开始杀怪

            proxy.killBoss();

            //升级

            proxy.upgrade();

            //记录结束游戏时间

            System.out.println("结束时间是:2009-8-26 03:40");

     }

}

这次能访问吗?还是不行,结果如下所示:

开始时间是:2009-8-25 10:45

请使用指定的代理访问

请使用指定的代理访问

请使用指定的代理访问

结束时间是:2009-8-26 03:40

同样是不能访问,为什么呢?它不是真实角色指定的对象,这个代理对象是你自己new出来的,当然真实对象不认了,这就好比是那个明星,人家已经告诉你去找她的代理人了,你随便找个代理人能成吗?你必须去找她指定的代理才成!我们修改一下场景类,如代码清单12-18所示。

<!--[if !supportLists]-->代码清单12-9              <!--[endif]-->强制代理的场景类

public class Client {

<!--[if !supportEmptyParas]--> <!--[endif]-->

     public static void main(String[] args) {

            //定义个游戏的角色

            IGamePlayer player = new GamePlayer("张三");

            //获得指定的代理

            IGamePlayer proxy = player.getProxy();

            //开始打游戏,记下时间戳

            System.out.println("开始时间是:2009-8-25 10:45");

            proxy.login("zhangSan", "password");

            //开始杀怪

            proxy.killBoss();

            //升级

            proxy.upgrade();

            //记录结束游戏时间

            System.out.println("结束时间是:2009-8-26 03:40");

     }

}

运行结果如下:

开始时间是:2009-8-25 10:45

登录名为zhangSan 的用户 张三登录成功!

张三在打怪!

张三 又升了一级!

结束时间是:2009-8-26 03:40

OK,可以正常访问代理了。强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色,高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成

原创粉丝点击