再说设计模式-代理模式的扩展
引言
我有一篇文章再说设计模式-代理模式,已经讲解了关于代理模式的基本概念,这里我们再来讨论一下代理模式的一些扩展知识。
在网络上代理服务设置分为透明代理和普通代理。透明代理就是用户不用关心设置代理服务器地址,就可以直接访问,也就是说代理服务器对于用户来说是透明的,不用知道它的存在;普通代理则是需要用户自己设置代理服务器IP地址,用户必须知道代理的存在。
而在我们设计模式中的普通代理
和强制代理
也是类似的一种结构。
普通代理
- 就是我们要知道代理的存在;
强制代理
- 调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的。
普通代理
为了简单说明,我们以游戏代练为例子(在玩游戏时,我们觉得升级很麻烦,所以找专门的游戏代练团队帮助升级)。 我自己做为一个游戏玩家,我肯定自己不练级了,也就是场景类(Client)不能直接new一个GramePlayer对象了,它必须由GamePlayerProxy来进行模拟场景,类图如下所示:

调用者只知道代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求的场合。
强制代理
强制代理在设计模式中比较特殊,为什么这么讲呢?因为一般的情况下,通过代理找到真实的角色,但是强制代理却是要强制
,你必须通过真实角色找到代理角色,否则你不能访问。甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色代理角色。
就好比你和大明星华仔很熟,相互认识,有件事你需要向他确认一下,于是你就直接拨通了华仔的电话:
"喂,华仔呀,我要见一个某某导演,你帮下忙呀!"
"不行呀,猿哥,我这几天很忙的,你找我的经纪人吧……"
so~~,你想直接绕过他的代理,谁知道返回的还是他的代理,这就是强制代理,你可以不用知道代理存在,但是你的所作所为还是需要代理为你提供。其类图如下所示:

// 游戏用户接口类
public interface IGamePlayer {
// 登录游戏
public void login(String user, String password);
// 杀怪
public void killBoss();
// 升级
public void upgrade();
// 每个人都可以找一下自己的代理
public IGamePlayer getProxy();
}
// 真实类
public class GamePlayer implements IGamePlayer {
private String name = "";
// 我的代理
private IGamePlayer proxy = null;
public GamePlayer(String _name) {
this.name = _name;
}
@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("请使用指定代理访问");
}
}
@Override
public IGamePlayer getProxy() {
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
// 校验是否是代理访问
private boolean isProxy() {
if (this.proxy == null) {
return false;
} else {
return true;
}
}
}
// 代理类
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();
}
@Override
public IGamePlayer getProxy() {
return this;
}
}
// 场景类
public class Client {
public static void main(String[] args) {
requestOfSubject();
System.out.println("~~~~~~~~~~~~~~分隔线~~~~~~~~~~~~~~~~");
requestOfProxy();
System.out.println("~~~~~~~~~~~~~~分隔线~~~~~~~~~~~~~~~~");
requestOfForceProxy();
}
/**
* 直接访问真实角色
*/
private static void requestOfSubject() {
IGamePlayer player = new GamePlayer("猿哥");
System.out.println("开始时间是:" + System.nanoTime());
player.login("oneape15", "pwd123");
player.killBoss();
player.upgrade();
System.out.println("游戏结束(直接访问真实角色)");
}
/**
* 直接访问代理类
*/
private static void requestOfProxy() {
IGamePlayer player = new GamePlayer("猿哥");
IGamePlayer proxy = new GamePlayerProxy(player);
System.out.println("开始时间是:" + System.nanoTime());
proxy.login("oneape15", "pwd123");
proxy.killBoss();
proxy.upgrade();
System.out.println("游戏结束(直接访问代理类)");
}
/**
* 使用强制代理的场景
*/
private static void requestOfForceProxy() {
IGamePlayer player = new GamePlayer("猿哥");
IGamePlayer proxy = player.getProxy();
System.out.println("开始时间是:" + System.nanoTime());
proxy.login("oneape15", "pwd123");
proxy.killBoss();
proxy.upgrade();
System.out.println("游戏结束(使用强制代理的场景)");
}
}
运行结果如下:

动态代理
动态代理是在实现阶段不用关心代理是谁,而在运行阶段才指定代理哪一个对象。相对来说,自己写代理类的方式就是静态代理。我们大家都知道的一个非常流行的名称叫面向横切面编程
,也就是AOP(Aspect Oriented Programming)
,其核心就是采用了动态代理机制。我们这里还以打游戏为例,其类图,修改如下:

其中InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理。
接口类:
public interface IGamePlayer {
// 登录游戏
public void login(String user, String password);
// 杀怪
public void killBoss();
// 升级
public void upgrade();
}
动态代理类:
public class GamePlayIH implements InvocationHandler {
// 被代理者
Class clazz = null;
// 被代理实例
Object obj = null;
// 我要代理谁
public GamePlayIH(Object _obj) {
this.obj = _obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(this.obj, args);
}
}
真实角色类:
public class GamePlayer implements IGamePlayer {
private String name = "";
// 我的代理
private IGamePlayer proxy = null;
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("猿哥");
// 定义一个Handler
InvocationHandler handler = new GamePlayIH(player);
// 开始打游戏,记下时间戳
System.out.println("开始时间: " + new Date());
// 获得类的class loader
ClassLoader cl = player.getClass().getClassLoader();
// 动态产生一个代理者
IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl, new Class[]{IGamePlayer.class}, handler);
// 登录
proxy.login("oneape15", "pwd123");
//开始杀怪
proxy.killBoss();
// 升级
proxy.upgrade();
// 结束
System.out.println("结果游戏: "+ new Date());
}
}
运行结果如下:

动态代理是根据被代理的接口生成所有方法,也就是说给定一个接口,动态代理会宣称我已经实现该接口下的所有方法了
。
接着上面说的AOP编程,AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限都可以在系统设计阶段不用考虑,而在设计后通过AOP的方式切过去。通用动态代理模型如下:

最佳实践
代理模式应用得非常广泛,大到一个系统框架,企业平台,小到代码片段、事务处理,稍不留意就用到代理模式。可能该模式是大家接触最多的模式,而且有了AOP大家写代理就更加简单了,有类似Spring AOP和AspectJ这样非常优秀的工具,拿来主义即可!