1.类图和角色
定义:Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问)。
代理模式涉及的角色有:
- 抽象主题角色:声明了真实主题和代理主题的共同接口,以便在任何可以使用真实主题的地方都可以使用代理角色。
- 代理主题(Proxy)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;与真实对象有共同的接口,以便可以在任何时候都可以代替真实对象;控制对真实对象的引用,负责在需要的时候操作真实的主题对象;通常,代理将调用传递给真实对象之前或之后,都要执行某些操作,而不是单纯的传递。
- 真实主题(RealSubject)角色:定义了代理所代表的真实对象。
2.示例代码
抽象主题的代码:
public abstract class Subject {
public abstract void request();
}
真实主题的代码:
public class RealSubject extends Subject {
public RealSubject() {}
public void request() {
System.out.println("Real subject");
}
}
代理主题角色的代码:可以看出,代理主题处理将所有的请求委派给真实主题角色之外,还在委派之前和之后分别执行了一个preRequest()和postRequest()。
public class ProxySubject extends Subject {
private RealSubject realSubject;
public ProxySubject() {}
public void request() {
preRequest();
if (realSubject == null) {
realSubject = new RealSubject();
}
//调用真实对象的业务方法
realSubject.request();
postRequest();
}
private void preRequest() {
//在执行request()之前,要执行的代码
}
private void postRequest() {
//在执行request()之后,要执行的代码
}
}
在使用代理主题的时候,要将变量声明类型声明为抽象主题的类型,而将真实的类型设为代理主题类型,代码如下:
Subject subject = new ProxySubject();
subject.request();
从以上的代码可以看出代理是怎样工作的,首先,代理主题不改变主题接口;其次,地理主题起到的是一个传递请求的作用;最后代理主题在传递请求之前和之后可以执行特定的操作,而不是单纯的传递请求。
3.代理模式的应用
代理模式在现实中的使用非常多,非常典型的应用就是Spring AOP。还有就是java web应用中的Filter,它也是非常典型的代理的应用。
4.代理模式的扩展
4.1普通代理
普通代理的要求就是客户端只能访问代理角色,而不能访问真实角色。我们以游戏代练的场景为例,游戏代练者与实际的玩家有共同的接口,游戏代练者使用玩家的账号登录游戏,升级打怪,真实玩家支付一定的费用给游戏代练者。类图如下:
玩家接口代码如下:
public interface IGamePlayer {
public void login(String user, String password);
public void killBoss();
public void upgrade();
}
玩家代码:
public class GamePlayer implements IGamePlayer {
private String name = "";
public GamePlayer(IGamePlayer gamePlayer, String name) {
if (gamePlayer == null) {
throw new RuntimeException("不能创建真实角色");
} else {
this.name = name;
}
}
@Override
public void killBoss() {
//打怪
}
@Override
public void login(String user, String password) {
//登录
}
@Override
public void upgrade() {
//升级
}
}
游戏代练者的代码:
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(String name) {
gamePlayer = new GamePlayer(this, name);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
调用者代码:
IGamePlayer proxy = new GamePlayerProxy("李四");
proxy.login("zhansan", "123");
proxy.killBoss();
proxy.upgrade();
调用者只知道代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实角色想怎么改就怎么改,对高层没有任何的影响。在实际的项目中,一般都是通过约定来禁止new一个真实的角色。
4.2强制代理
强制代理是要强制必须通过真实角色查找到代理角色,否则不能访问。无论是通过代理类还是通过new生成一个真实角色,都不能访问,只能通过真实角色指定的代理类才可以访问。也就是说,客户端new一个真实角色对象,返回的却是代理角色。类图如下:
强制代理的接口代码
public interface IGamePlayer {
public void login(String name, String password);
public void killBoss();
public void upgrade();
public IGamePlayer2 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 name, String password) {
if (isProxy()) {
System.out.println(this.name + "登录了!");
} else {
System.out.println("请使用代理访问");
}
}
@Override
public void killBoss() {
if (isProxy()) {
System.out.println(this.name + "打怪!");
} else {
System.out.println("请使用代理访问");
}
}
@Override
public void upgrade() {
if (isProxy()) {
System.out.println(this.name + "升级了!");
} else {
System.out.println("请使用代理访问");
}
}
@Override
public IGamePlayer getProxy() {
this.proxy = new GamePlayerProxy2(this);
return this.proxy;
}
private boolean isProxy() {
return (this.proxy == null ? false : true);
}
}
增加了一个私有方法,用了检查是否是自己指定的代理对象。再来看看代理角色的代码:
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
@Override
public void login(String name, String password) {
this.gamePlayer.login(name, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
@Override
public IGamePlayer2 getProxy() {
return this;
}
}
客户端调用代码:
//通过真实主题角色获取代理,再访问方法
IGamePlayer2 gamePlayer = new GamePlayer2("张三");
IGamePlayer2 proxy = gamePlayer.getProxy();
proxy.login("zhansan", "123");
proxy.upgrade();
proxy.killBoss();
//真实对象直接访问
IGamePlayer2 gamePlayer2 = new GamePlayer2("张三");
gamePlayer2.killBoss();
gamePlayer2.login("lili", "123");
运行代码可见,通过真实对象是不能直接访问方法的。由代码可以看出,强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。