代码改变世界

结构类模式(七):代理(Proxy)

2016-10-27 14:35  阿诚de窝  阅读(382)  评论(0编辑  收藏  举报

定义

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

代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制。

代理类负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

基本上可以理解为:代理类持有实际操作对象的引用,通过公开方法将这些引用的方法提供给其它类调用。

和其它模式的区别

  1. 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理迷失不能改变所代理类的接口。
  2. 和装饰模式的区别:装饰模式为了增强功能,而代理模式是为了加以控制。

UML

优点

  1. 真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。
  2. 具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。

缺点

  1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

应用场景

远程代理

为一个对象在不同的地址空间提供局部代表。

虚代理

根据需要创建开销很大的对象。比如浏览网页时,图片可以使用代理先按宽高进行占位,下载好再进行显示。

保护代理

控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。

智能指针

取代了简单的指针,它在访问对象时执行一些附加操作。

它的典型用途包括:

  1. 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它。
  2. 当第一次引用一个持久对象时,将它装入内存。
  3. 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

示例

游戏玩家(被代练对象)和游戏代练者(代理对象)的示例,其中如果游戏玩家升级则游戏代练则要进行收费。

C++

 

C#

  1 using System;
  2 
  3 namespace DesignPattern
  4 {
  5     class Program
  6     {
  7         static void Main(string[] args)
  8         {
  9             //李雷自己打游戏
 10             GamePlayer liLei = new GamePlayer("李雷");
 11             liLei.Login();
 12             liLei.KillMonster();
 13             liLei.Upgrade();
 14 
 15             Console.WriteLine();
 16 
 17             //李雷花钱请游戏代练帮其升级
 18             Leveling leveling = new Leveling(liLei);
 19             leveling.Login();
 20             leveling.KillMonster();
 21             leveling.Upgrade();
 22 
 23             Console.Read();
 24         }
 25     }
 26 
 27     /// <summary>
 28     /// 游戏玩家接口.
 29     /// </summary>
 30     public interface IGamePlayer
 31     {
 32         /// <summary>
 33         /// 登录.
 34         /// </summary>
 35         void Login();
 36 
 37         /// <summary>
 38         /// 打怪.
 39         /// </summary>
 40         void KillMonster();
 41 
 42         /// <summary>
 43         /// 升级.
 44         /// </summary>
 45         void Upgrade();
 46     }
 47 
 48     /// <summary>
 49     /// 玩家类.
 50     /// </summary>
 51     public class GamePlayer : IGamePlayer
 52     {
 53         private string _name;
 54 
 55         public GamePlayer(string name)
 56         {
 57             _name = name;
 58         }
 59 
 60         public void Login()
 61         {
 62             Console.WriteLine("玩家\"" + _name + "\"登录游戏。");
 63         }
 64 
 65         public void KillMonster()
 66         {
 67             Console.WriteLine("玩家\"" + _name + "\"开始打怪。");
 68         }
 69 
 70         public void Upgrade()
 71         {
 72             Console.WriteLine("玩家\"" + _name + "\"等级提升一级。");
 73         }
 74     }
 75 
 76     /// <summary>
 77     /// 游戏代练类.
 78     /// </summary>
 79     public class Leveling : IGamePlayer
 80     {
 81         private GamePlayer _gamePlayer;
 82 
 83         public Leveling(GamePlayer gamePlayer)
 84         {
 85             _gamePlayer = gamePlayer;
 86         }
 87 
 88         public void Login()
 89         {
 90             _gamePlayer.Login();
 91         }
 92 
 93         public void KillMonster()
 94         {
 95             _gamePlayer.KillMonster();
 96         }
 97 
 98         public void Upgrade()
 99         {
100             _gamePlayer.Upgrade();
101 
102             Console.WriteLine("游戏代练者收费。");
103         }
104     }
105 }
View Code

Java

Java的示例使用延迟代理和动态代理查询天气的例子。

  1 public class Main
  2 {
  3     public static void main(String[] args)
  4     {
  5         //不使用代理
  6         IWeather weather1 = new ChinaWeather();
  7         System.out.println(weather1.getWeatherByCity("上海"));
  8         
  9         //延迟代理
 10         IWeather weather2 = new ChinaWeatherDelayProxy();
 11         System.out.println(weather2.getWeatherByCity("北京"));
 12         
 13         //动态代理
 14         IWeather weather3 = new WeatherDynamicProxy(new InternationalWeather());
 15         System.out.println(weather3.getWeatherByCity("北京"));
 16     }
 17 
 18     /**
 19      * 天气查询接口
 20      */
 21     public interface IWeather
 22     {
 23         /**
 24          * 获取指定城市的天气情况
 25          */
 26         String getWeatherByCity(String city);
 27         
 28         /**
 29          * 请求查询
 30          */
 31         void request();
 32     }
 33 
 34     /**
 35      * 使用中国的天气服务器查询天气情况,只能查询到中国的天气
 36      */
 37     public static class ChinaWeather implements IWeather
 38     {
 39         public ChinaWeather()
 40         {
 41             this.request();
 42         }
 43         
 44         @Override
 45         public String getWeatherByCity(String city)
 46         {
 47             if(city.equals("北京"))
 48             {
 49                 return "晴 28度 PM2.5 20";
 50             }
 51             if(city.equals("上海"))
 52             {
 53                 return "晴 33度 PM2.5 10";
 54             }
 55             return "未知";
 56         }
 57 
 58         @Override
 59         public void request()
 60         {
 61             System.out.println("请求中国的天气服务器,解析其格式得到信息");
 62         }
 63     }
 64 
 65     /**
 66      * 使用国际的天气服务器查询天气情况,当然也可以查询到中国的天气了
 67      */
 68     public static class InternationalWeather implements IWeather
 69     {
 70         public InternationalWeather()
 71         {
 72             this.request();
 73         }
 74         
 75         @Override
 76         public String getWeatherByCity(String city)
 77         {
 78             if(city.equals("北京"))
 79             {
 80                 return "晴 29度 PM2.5 500+";
 81             }
 82             if(city.equals("上海"))
 83             {
 84                 return "晴 32度 PM2.5 500+";
 85             }
 86             return "未知";
 87         }
 88 
 89         @Override
 90         public void request()
 91         {
 92             System.out.println("请求国际的天气服务器,解析其格式得到信息");
 93         }
 94     }
 95 
 96     /**
 97      * 延迟代理
 98      */
 99     public static class ChinaWeatherDelayProxy implements IWeather
100     {
101         private IWeather weather;
102         
103         private IWeather getWeather()
104         {
105             if(weather == null)
106             {
107                 weather = new ChinaWeather();
108             }
109             return weather;
110         }
111         
112         @Override
113         public String getWeatherByCity(String city)
114         {
115             return getWeather().getWeatherByCity(city);
116         }
117 
118         @Override
119         public void request()
120         {
121             getWeather().request();
122         }
123     }
124 
125     /**
126      * 动态代理
127      */
128     public static class WeatherDynamicProxy implements IWeather
129     {
130         private IWeather weather;
131         
132         public WeatherDynamicProxy(IWeather weather)
133         {
134             this.weather = weather;
135         }
136 
137         @Override
138         public String getWeatherByCity(String city)
139         {
140             return weather.getWeatherByCity(city);
141         }
142 
143         @Override
144         public void request()
145         {
146             weather.request();
147         }
148     }
149 }
View Code

AS3

 

我的经验总结

在PureMVC框架中,其Model层使用了Proxy的设计模式。

我们先看看Model层的主要功能:

  1. 保存程序数据;
  2. 远程消息发送及请求。

在我们的游戏中,分别存在UserData及SocketConnection这两个对象,其中UserData用来存储用户数据,SocketConnection用来处理所有远程消息的接收和发送,而每个模块的Proxy都是对这两个对象的代理,其提供该模块需要的数据和操作接口给该模块,比如技能模块中,Proxy会提供技能相关的数据和协议给到该模块的View层。代理类为模块提供了屏蔽不需要的接口的功能。

然而在PureMVC的升级版框架RobotLegs中,其定义的是MVCS的框架,Model层直接设计为Model+Services的组合,没有使用代理模式,由于没有代理模式,所以中介类中就不是直接操作代理类,而一般情况是通过发送Command,在Command中进行处理。