代理模式

为其它对象提供一种代理以控制这个对象的访问。

简单的举个例子,可以看作一个玩家自己不打游戏,而是通过找代练让代练去操作游戏。诸如Windows上的快捷方式,其实都是一种代理模式。

优缺点

优点

  • 职责清晰。真实的角色就是实现实际的业务逻辑,不用担心其他非本职责的事务。
  • 高扩展性。代理类完全可以在不做任何修改的情况下使用。
  • 智能化。比如动态代理。

缺点

  • 有些类型的代理模式可能会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

结构图

UML框图
UML框图

代理模式中有以下角色:

  • 抽象主题类(Subject): 声明真实主题和代理Proxy 的共同接口方法。
  • 真实主题类(RealSubject): 具体主题角色,定义了 Proxy 所代表的真实实体
  • 代理类(Proxy):持有对真实主题类的引用,使代理可以访问实体,并提供一个与 Subject 的接口相同的接口。
  • 客户端类。

代码示例

首先我们来看一下对应基本结构下的代理模式。

Subject抽象类

定义了 RealSubject 和 Proxy 的共同接口,这样就在任何使用 RealSubject 的地方都可以使用Proxy。

1
2
3
public abstract class Subject {
public abstract void request();
}

RealSubject类

1
2
3
4
5
6
public class RealSubject extends Subject {
@Override
public void request() {
System.out.println("真实的请求RealSubject");
}
}

Proxy类

代理类。一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代理哪个真实主题角色,是由场景类决定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Proxy extends Subject {

private RealSubject realSubject = null;

public Proxy() {
this.realSubject = new RealSubject();
}

@Override
public void request() {
this.realSubject.request();
}

}

Client 客户端

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}

}

静态代理

所谓静态代理类是指:由程序员创建或由特定工具自动生成源代码,再对其进行编译。在程序运行之前,代理类的.class文件就已经存在了。

抽象主题类

1
2
3
public interface IShop {
void buy();
}

真实主题类

1
2
3
4
5
6
public class Allenmistake implements IShop {
@Override
public void buy() {
...
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
public class Purchasing implements IShop {
private IShop mShop;
public Purchasing(IShop shop) {
this.mShop = shop;
}

@Override
public void buy() {
mShop.buy();
}
}

4、客户端类

1
2
3
4
5
6
7
8
public class Clent {

public static void main(String[] args) {
IShop allenmistake = new Allenmistake();
IShop purchasing = new Purchasing(allenmistake);
purchasing.buy();
}
}

动态代理

在代码运行时通过反射来动态地生成代理类的对象,并确定到底来代理谁。

通过动态代理,我们不再需要手动创建代理类,只需编写一个动态处理器即可,而真正的代理对象由JDK在运行时帮我们创建。所以我们也将之称为JDK动态代理。

方法步骤如下:(重点

写一个代理类实现 InvocationHandler 接口,通过构造函数把代理对象(具体目标类)传入到此处理器中,在invoke()方法中增加method.invoke(realSubject, args)。在调用方法时,通过java.lang.reflect.ProxynewProxyInstance() 来获取代理实现类,生成代理对象时,直接调用方法即可。真实主题类发生变化时,由于它实现了公用的接口,因此代理类不需要修改。

改写静态代理的代理类和客户端类,如下所示:

动态代理类

1
2
3
4
5
6
7
8
9
10
11
public class DynamicPurchasing implements InvocationHandler {
private Object obj;
public DynamicPurchasing(Object obj) {
this.obj = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obj, args);
}
}

客户端类

1
2
3
4
5
6
7
8
9
10
11
public class Clent {

public static void main(String[] args) {
IShop jsonChao = new JsonChao();
DynamicPurchasing mDynamicPurchasing = new DynamicPurchasing(jsonChao);
ClassLoader cl = jsonChao.getClass.getClassLoader();
// 生成代理类对象
IShop purchasing = Proxy.newProxyInstance(cl, new Class[]{IShop.class}, mDynamicPurchasing);
purchasing.buy();
}
}