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

时间:2022-07-12 17:27:33

定义

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

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

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

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

和其它模式的区别

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

UML

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

优点

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

缺点

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

应用场景

远程代理

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

虚代理

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

保护代理

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

智能指针

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

它的典型用途包括:

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

示例

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

C++

C#

 using System;

 namespace DesignPattern
{
class Program
{
static void Main(string[] args)
{
//李雷自己打游戏
GamePlayer liLei = new GamePlayer("李雷");
liLei.Login();
liLei.KillMonster();
liLei.Upgrade(); Console.WriteLine(); //李雷花钱请游戏代练帮其升级
Leveling leveling = new Leveling(liLei);
leveling.Login();
leveling.KillMonster();
leveling.Upgrade(); Console.Read();
}
} /// <summary>
/// 游戏玩家接口.
/// </summary>
public interface IGamePlayer
{
/// <summary>
/// 登录.
/// </summary>
void Login(); /// <summary>
/// 打怪.
/// </summary>
void KillMonster(); /// <summary>
/// 升级.
/// </summary>
void Upgrade();
} /// <summary>
/// 玩家类.
/// </summary>
public class GamePlayer : IGamePlayer
{
private string _name; public GamePlayer(string name)
{
_name = name;
} public void Login()
{
Console.WriteLine("玩家\"" + _name + "\"登录游戏。");
} public void KillMonster()
{
Console.WriteLine("玩家\"" + _name + "\"开始打怪。");
} public void Upgrade()
{
Console.WriteLine("玩家\"" + _name + "\"等级提升一级。");
}
} /// <summary>
/// 游戏代练类.
/// </summary>
public class Leveling : IGamePlayer
{
private GamePlayer _gamePlayer; public Leveling(GamePlayer gamePlayer)
{
_gamePlayer = gamePlayer;
} public void Login()
{
_gamePlayer.Login();
} public void KillMonster()
{
_gamePlayer.KillMonster();
} public void Upgrade()
{
_gamePlayer.Upgrade(); Console.WriteLine("游戏代练者收费。");
}
}
}

Java

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

 public class Main
{
public static void main(String[] args)
{
//不使用代理
IWeather weather1 = new ChinaWeather();
System.out.println(weather1.getWeatherByCity("上海")); //延迟代理
IWeather weather2 = new ChinaWeatherDelayProxy();
System.out.println(weather2.getWeatherByCity("北京")); //动态代理
IWeather weather3 = new WeatherDynamicProxy(new InternationalWeather());
System.out.println(weather3.getWeatherByCity("北京"));
} /**
* 天气查询接口
*/
public interface IWeather
{
/**
* 获取指定城市的天气情况
*/
String getWeatherByCity(String city); /**
* 请求查询
*/
void request();
} /**
* 使用中国的天气服务器查询天气情况,只能查询到中国的天气
*/
public static class ChinaWeather implements IWeather
{
public ChinaWeather()
{
this.request();
} @Override
public String getWeatherByCity(String city)
{
if(city.equals("北京"))
{
return "晴 28度 PM2.5 20";
}
if(city.equals("上海"))
{
return "晴 33度 PM2.5 10";
}
return "未知";
} @Override
public void request()
{
System.out.println("请求中国的天气服务器,解析其格式得到信息");
}
} /**
* 使用国际的天气服务器查询天气情况,当然也可以查询到中国的天气了
*/
public static class InternationalWeather implements IWeather
{
public InternationalWeather()
{
this.request();
} @Override
public String getWeatherByCity(String city)
{
if(city.equals("北京"))
{
return "晴 29度 PM2.5 500+";
}
if(city.equals("上海"))
{
return "晴 32度 PM2.5 500+";
}
return "未知";
} @Override
public void request()
{
System.out.println("请求国际的天气服务器,解析其格式得到信息");
}
} /**
* 延迟代理
*/
public static class ChinaWeatherDelayProxy implements IWeather
{
private IWeather weather; private IWeather getWeather()
{
if(weather == null)
{
weather = new ChinaWeather();
}
return weather;
} @Override
public String getWeatherByCity(String city)
{
return getWeather().getWeatherByCity(city);
} @Override
public void request()
{
getWeather().request();
}
} /**
* 动态代理
*/
public static class WeatherDynamicProxy implements IWeather
{
private IWeather weather; public WeatherDynamicProxy(IWeather weather)
{
this.weather = weather;
} @Override
public String getWeatherByCity(String city)
{
return weather.getWeatherByCity(city);
} @Override
public void request()
{
weather.request();
}
}
}

AS3

我的经验总结

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

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

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

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

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