代理模式

代理模式: 为一个对象提供一个替身,以控制对目标对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,及扩展目标对象的功能。

被代理的对象可以是远程对象,创建开销大的对象或需要安全控制的对象。

在现实生活中,一个对象不能直接访问另一个对象,这时需要找中介来访问目标对象,此时的中介就是代理对象。例如:租房子时,我们无法与房东取得联系,只能通过某网站与中介进行交易,获取自己心仪的房间等等。

代理模式结构

  • 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。代理必须遵循该接口才能伪装成服务对象。
  • 真实主题(Real Subject)类:实现了出现主题中的具体业务,是代理对象所代理的真实对象,是最终要引用的对象。
  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
  • 客户端 (Client) 能通过同一接口与服务或代理进行交互, 所以你可在一切需要服务对象的代码中使用代理。

现实中的例子

我们模拟在现实生活通过携程火车票系统购票的过程,假设我们通过代理购票系统购票时,购票系统首先会校验购票人的购票资格,校验成功则会调用真正的购票系统进行购票,购票成功后会收取购票人的一定的服务费。

程序化示例

1、抽象主题类:代理类与被代理类都需要实现的接口。

1
2
3
4
5
public interface Ticketing {

void buyTicket();

}

2、真实主题类: 目标类

1
2
3
4
5
6
public class RailwaySite implements Ticketing {
@Override
public void buyTicket() {
System.out.println("调用官方系统购票成功!");
}
}

3、代理类:需要实现被代理类的接口,使用代理方法调用目标对象的方法,同时实现对目标方法的扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ProxyTicketSystem implements Ticketing {

private Ticketing ticket;

public ProxyTicketSystem(Ticketing ticket) {
this.ticket = ticket;
}

@Override
public void buyTicket() {
preHandle();
ticket.buyTicket();
postHandle();
}

private void preHandle() {
System.out.println("代理(携程火车票)系统校验购票人资格!");
System.out.println("校验成功!");
}

private void postHandle() {
System.out.println("代理(携程火车票)系统收取购票人服务费10元!");
}

}

4、客户端:需要创建被代理对象和代理对象,并进行组合调用。

1
2
3
4
5
6
7
8
9
public class Client {

public static void main(String[] args) {
RailwaySite railwaySite = new RailwaySite();
ProxyTicketSystem proxy = new ProxyTicketSystem(railwaySite);
proxy.buyTicket();
}

}

程序输出:

1
2
3
4
代理(携程火车票)系统校验购票人资格!
校验成功!
调用官方系统购票成功!
代理(携程火车票)系统收取购票人服务费10元!

适用性

当需要比简单指针更通用或更复杂的对象引用时,就需要使用代理模式。以下是代理模式适用的几种常见情况。

  • 远程代理为不同地址空间的对象提供本地代表。
  • 虚拟代理按需创建昂贵的对象。
  • 保护代理控制对原始对象的访问。当对象应具有不同访问权限时,保护代理非常有用。

已知使用

  • java.lang.reflect.Proxyopen in new window
  • Apache Commons Proxy