代理模式是什么?
代理模式(Proxy Pattern)是一种结构型的设计模式,它为目标对象提供一个替代品或占位符,以控制对这个对象的访问。在代理模式中,一个请求会有两个对象参与处理:由代理对象接收客户端的请求,并将请求委托给目标对象处理,代理对象可以控制请求的访问,还可以在请求委托给目标对象之前或之后进行一些处理。
UML 类图
用 UML 类图来描述代理模式的结构,在模式中各个角色之间的关系:
根据上图,总结了模式中各个角色的职责以及它们之间的关系:
- 服务接口声明了服务的接口,代理需要遵循该接口才能伪装成服务对象。
- 服务实现了服务接口,并完成了一些业务逻辑。
- 代理会包含一个服务对象的引用,代理会将请求转发给服务对象来处理,在转发请求的前后,它可以做一些额外的操作,例如访问控制、记录日志、缓存结果等。
- 客户端仅与服务接口交互,它并不需要知道它是在与代理或是服务交互。
代理模式的类型
我们知道,代理模式会创建一个代理对象,让代理对象控制某个对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或者需要安全控制的对象。根据被代理的对象,我们可以对代理作如下分类:
- 远程代理可以作为另一个 JVM 对象的本地代表,典型的案例就是 RPC 框架,调用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果返回给客户端。
- 虚拟代理作为创建开销大的对象的代表。若一个对象的创建非常耗时,我们会在当真正需要它的时候才去创建。在对象的创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。
- 控制代理在将请求转发给服务对象之前,会进行权限控制。
案例
让我们通过一个案例来帮助我们进一步理解代理模式。看过 NBA 的朋友都知道,现在国内的 NBA 网络直播是由腾讯独家代理的。即使我们不到现场观看比赛,也能够通过网络直播观看精彩的比赛。这就是一个典型的代理模式案例,我们通过腾讯 NBA 观看了赛事直播。除了腾讯,还有很多电视台会购买 NBA 比赛的转播权,我们定义一个服务接口来表示直播比赛。
public interface NBALive {
void liveGame(Team homeTeam, Team visitingTeam);
}
NBA TV 负责现场赛事的转播。
public class NBATV implements NBALive {
@Override
public void liveGame(Team homeTeam, Team visitingTeam) {
System.out.println("Welcome to the game live, " + visitingTeam + " vs. " + homeTeam);
}
}
腾讯 NBA 频道负责转播 NBA TV 的比赛内容,但并不是所有的比赛都可以免费观看,有些场次是要购买会员,才能观看的。
public class TencentNBAChannel implements NBALive {
private Visitor visitor;
private NBALive nbaLive;
public TencentNBAChannel(Visitor visitor) {
this.visitor = visitor;
this.nbaLive = new NBATV();
}
@Override
public void liveGame(Team homeTeam, Team visitingTeam) {
if (visitor.isVIP()) {
System.out.println("Dear member, Welcome to Tencent NBA.");
nbaLive.liveGame(homeTeam, visitingTeam);
} else {
System.out.println("Sorry, you are not the VIP.");
}
}
}
最后,我们通过一个用例来模拟一下观众在腾讯上观看 NBA 直播的过程。
public class ProxyMain {
public static void main(String[] args) {
Visitor huey = new Visitor();
NBALive tencentNBAChannelForHuey = new TencentNBAChannel(huey);
tencentNBAChannelForHuey.liveGame(Team.ROCKETS, Team.LAKERS);
huey.buyMemberships();
tencentNBAChannelForHuey.liveGame(Team.ROCKETS, Team.LAKERS);
}
}
控制输出内容如下:
Sorry, you are not the VIP.
Dear member, Welcome to Tencent NBA.
Welcome to the game live, Lakers vs. Rockets
案例源码
可在 GitHub 上查看上述案例的完整代码。
参考资料
本文参考的资料如下:
- Head First Design Patterns - Elisabeth Freeman, Chapter 11.
- https://www.baeldung.com/java-proxy-pattern
- https://refactoring.guru/design-patterns/proxy
- https://refactoringguru.cn/design-patterns/proxy