代理模式
1、代理模式的动机
- 客户端不想直接或者不能直接引用一个对象时,可以通过引入一个第三者来间接访问对象,并且可以通过第三者即代理对象添加或者去掉一些操作;
- 通过第三者来操作对象或者作为对象的替身,这种机制就是代理模式,通过引入代理访问真实对象;
2、代理模式的定义
代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用,它是一种对象结构型模式;
3、代理模式的类结构图
分析如下:
- Subject抽象主题角色:既可以为抽象类,也可以为接口,是一个最普通的业务类型定义;
- RealSubject具体主题角色:也叫做被委托对象,被代理对象,是业务逻辑的正真执行者;
- Proxy代理主题角色:也叫委托类,代理类,负责对真实主题角色的应用,把所有的抽象主题定义的方法委托给真实对象去处理,并在处理前后加预处理工作;
4、优缺点和适用场景
优点:
- 职责清晰,真实角色只负责业务处理,而不必关系其他的东西,后期的事交给代理类来实现;
- 高扩展性,真实角色随时可以发生变化,但是其一定会实现抽象主题,那我们的代理就可以在不用修改的前提下使用;
- 智能化,主要体现在动态代理;
- 代理可以对真实对象进行权限保护也可以增加预处理命令;
缺点:
- 客户端到真实主题之间增加了代理,会造成请求的时间的延迟;
- 较为复杂的代理实现起来需要很多额外的工作;
适用场景:
- 远程代理,为不在同一地址空间的对象创建一个代理,用于对该对象的访问;
- 虚拟代理,先为资源消耗比较大的对象创建一个代理,当真正需要使用该对象时在创建对象,可以有效的节省资源消耗,提高资源利用率;
- Copy-on-Write代理,它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆;
- 保护代理,可以为不同的用户提供不同的访问权限;
- 缓冲代理,为某一执行的结果提供临时的存储空间,以便多个客户端共享该结果;
- 防火墙代理,保护对象不被恶意对象接近;
- 智能引用代理,对对象的引用提供额外的操作,例如记录该对象的访问次数;
5、实例
5.1 普通代理
特点: 客户端必须知道代理存在,且只能通过代理对象访问真实对象,不能够直接访问真实对象;
类图:
抽象主题
public interface IGamePlayer {
// 登录
void login(String user, String password);
// 打怪
void killBoss();
// 升级
void upgrade();
}
具体主题
public class GamePlayer implements IGamePlayer {
private String name;
public GamePlayer(String name) {
this.name = name;
}
public void login(String user, String password) {
System.out.println("登录名为"+user+"的用户"+this.name + "登录成功");
}
public void killBoss() {
System.out.println(this.name + "在打怪");
}
public void upgrade() {
System.out.println(this.name + "在升级");
}
}
主题代理
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer;
public GamePlayerProxy(String gamePlayer) {
this.gamePlayer = new GamePlayer(gamePlayer);
}
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
public void killBoss() {
this.gamePlayer.killBoss();
}
public void upgrade() {
this.gamePlayer.upgrade();
}
}
客户端
public class GeneralProxyClient {
public static void main(String[] args) {
IGamePlayer proxy = new GamePlayerProxy("郑印");
proxy.login("XXXX", "134138");
proxy.killBoss();
proxy.upgrade();
}
}
5.2 强制代理
特点: 必须通过真实对象找到代理对象才能访问真实对象,否则无论是创建一个代理或者一个真实对象都不能访问真实对象。代理对象是通过真实对象来管理的;
类图
抽象主题
public interface IGamePlayerV2 extends IGamePlayer {
IGamePlayerV2 getProxy();
}
真实主题
public class GamePlayerV2 implements IGamePlayerV2 {
private IGamePlayerV2 gamePlayerV2;
private String name;
public GamePlayerV2(String name) {
this.name = name;
}
public void login(String user, String password) {
if (gamePlayerV2 != null) {
System.out.println("登录名为" + user + "的用户" + this.name + "登录成功");
} else {
System.out.println("请用指定代理访问");
}
}
public void killBoss() {
if (gamePlayerV2 != null) {
System.out.println(this.name + "在打怪");
} else {
System.out.println("请用指定代理访问");
}
}
public void upgrade() {
if (gamePlayerV2 != null) {
System.out.println(this.name + "在升级");
} else {
System.out.println("请用指定代理访问");
}
}
public IGamePlayerV2 getProxy() {
this.gamePlayerV2 = new GamePlayerV2Proxy(this);
return this.gamePlayerV2;
}
}
代理主题
public class GamePlayerV2Proxy implements IGamePlayerV2 {
private IGamePlayerV2 realPlayer;
public GamePlayerV2Proxy(IGamePlayerV2 realPlayer) {
this.realPlayer = realPlayer;
}
public void login(String user, String password) {
realPlayer.login(user,password);
}
public void killBoss() {
realPlayer.killBoss();
}
public void upgrade() {
realPlayer.upgrade();
}
public IGamePlayerV2 getProxy() {
return this;
}
}
客户端测试
public class ForceProxyClient {
public static void main(String[] args) {
IGamePlayerV2 gamePlayerV2 = new GamePlayerV2("郑印");
IGamePlayerV2 proxy = gamePlayerV2.getProxy();
proxy.login("XXXX", "1234");
proxy.killBoss();
proxy.upgrade();
}
}
5.3 动态代理
特点: 在实现阶段不用关心代理谁,只在运行阶段才指定代理谁。相对来说,自己写代理类就是静态代理;
类图
代理处理类
public class DynamicProxyHandle implements InvocationHandler {
private Object object;
public DynamicProxyHandle(Object object) {
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
}
invoke方法完成对真实对象方法的调用;
客户端
public class DynamicProxyClient {
public static void main(String[] args) {
GamePlayer iGamePlayer = new GamePlayer("郑印");
DynamicProxyHandle dynamicProxyHandle = new DynamicProxyHandle(iGamePlayer);
IGamePlayer proxy = (IGamePlayer) Proxy
.newProxyInstance(iGamePlayer.getClass().getClassLoader(), iGamePlayer.getClass().getInterfaces(), dynamicProxyHandle);
proxy.login("XXXX", "134138");
proxy.killBoss();
proxy.upgrade();
}
}
- 上一篇 类和类,类和接口,接口和接口之间的关系
- 下一篇 建造者模式