1. 定义
为其他对象提供一种代理以控制对这个对象的访问。
代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象后,对客户端并没有什么影响,就跟得到了真实对象一样来使用。
当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象。
2. 类图
Proxy: 代理对象,通常包含了以下功能,实现了与具体目标对象一样的借口,保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象,可以控制对具体目标对象的访问,并可以负责创建和删除它。
Subject: 目标对象,定义代理和具体目标对象的借口。
RealSubject: 具体目标对象,真正实现目标对象要求的功能。
3. 实例
package com.jerry.designpattern.proxy; /** * * @author Jerry * @date 2015年1月21日 上午10:39:09 */ public interface Player { void killMonster(); } package com.jerry.designpattern.proxy; /** * * @author Jerry * @date 2015年1月21日 上午10:46:10 */ public class PlayerImpl implements Player{ /** * 经验值 experience points */ private int exp; /** * 等级 */ private int level; public PlayerImpl() { level = 1; } @Override public void killMonster() { // TODO Auto-generated method stub System.out.println("杀死一个怪物,获得50点经验"); exp += 50; upgrade(); } /** * 升级 */ private void upgrade() { // TODO Auto-generated method stub if (exp % 100 == 0) { this.level++; System.out.println("恭喜你升级了,当前等级为: " + this.level); } } } package com.jerry.designpattern.proxy; /** * * @author Jerry * @date 2015年1月21日 上午11:05:01 */ public class GameProxy implements Player{ private Player player; public GameProxy(Player player) { this.player = player; } @Override public void killMonster() { // TODO Auto-generated method stub System.out.println("游戏公式代理升级,一次升10级"); for (int i = 0; i < 20; i++) { player.killMonster(); } } } package com.jerry.designpattern.proxy; /** * * @author Jerry * @date 2015年1月21日 上午11:00:54 */ public class Client { public static void main(String[] args) { /* * 不使用游戏代理,自己手动杀怪升级 */ Player player1 = new PlayerImpl(); player1.killMonster(); player1.killMonster(); player1.killMonster(); player1.killMonster(); /* * 花费money找代理升级 * 一次升10级 */ Player player2 = new PlayerImpl(); Player proxy = new GameProxy(player2); proxy.killMonster(); proxy.killMonster(); } }
4. 代理的分类
代理大致可以分为:
虚代理:根据需要来创建开销很大的对象,该对象只有在真正使用的时候才被创建。
远程代理:用来在不同地址空间上代表相同的一个对象,这个不同地址空间可以在本机上,也可以在其他机器上。在Java中最经典的是RMI
copy-on-write代理:在客户端操作的时候,只有对象确实改变了,才会真正的拷贝或克隆一个目标对象,算是虚代理的一个分支。
保护代理:控制原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制对原始对象的访问。
Cache代理:为那些昂贵操作的结构提供临时空间,以便多个客户端可以共享这些结果。
防火墙代理:保护对象不被恶意用户访问和操作。
同步代理:使多个用户能够同时访问目标对象而没有冲突。
智能指引:在访问对象时执行一些附加的操作。
5. 动态代理
Java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy和一个InvacationHandler接口,通常前面自己实现的代理模式称为静态代理,使用Java提供的功能实现的代理模式称为动态代理,
package com.jerry.designpattern.dynamicproxy; /** * * @author Jerry * @date 2015年1月21日 上午10:39:09 */ public interface Player { void killMonster(); } package com.jerry.designpattern.dynamicproxy; /** * * @author Jerry * @date 2015年1月21日 上午10:46:10 */ public class PlayerImpl implements Player{ /** * 经验值 experience points */ private int exp; /** * 等级 */ private int level; public PlayerImpl() { level = 1; } @Override public void killMonster() { // TODO Auto-generated method stub System.out.println("杀死一个怪物,获得50点经验"); exp += 50; upgrade(); } /** * 升级 */ private void upgrade() { // TODO Auto-generated method stub if (exp % 100 == 0) { this.level++; System.out.println("恭喜你升级了,当前等级为: " + this.level); } } } package com.jerry.designpattern.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * * @author Jerry * @date 2015年1月21日 上午11:13:19 */ public class GameProxy implements InvocationHandler{ public Player player; public GameProxy(Player player) { this.player = player; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("使用游戏代理升级,一次升10级"); for (int i = 0; i < 20; i++) { method.invoke(player, args); } return null; } public Player getInstance() { return (Player)Proxy.newProxyInstance(player.getClass().getClassLoader(), player.getClass().getInterfaces(), this); } } package com.jerry.designpattern.dynamicproxy; /** * * @author Jerry * @date 2015年1月21日 上午11:20:16 */ public class Client { public static void main(String[] args) { Player player = new PlayerImpl(); GameProxy gameProxy = new GameProxy(player); Player playerProxy = gameProxy.getInstance(); playerProxy.killMonster(); } }