定义
代理模式(Proxy Pattern):给某一个对象提供一个代理或占位符,并由代理对象来控制对象的访问。
结构
- Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
- Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供了一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制真实主题的使用,负责在需要的时候创建和创建真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
- RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色简介调用真实主题角色中定义操作。
代码
Subject
public abstract class Subject {
public abstract void request();
}
RealSubject
public class RealSubject extends Subject {
@Override
public void request() {
System.out.println("RealSubject");
}
}
Proxy
public class Proxy extends Subject {
private Subject subject = new RealSubject();
@Override
public void request() {
preRequest();
subject.request();
postRequest();
}
public void preRequest() {
System.out.println("preRequest");
}
public void postRequest() {
System.out.println("postRequest");
}
}
Test
public class Test {
public static void main(String[] args) {
Subject proxy = new Proxy();
proxy.request();
}
}
Java代理模式
- Proxy
- public static Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces):该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组。
- public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):该方法用于返回一个动态创建的代理类的实例,第一个参数loader代表代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表,第三个参数h表示所指派的调用处理程序类。
- InvocationHandler
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable:该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供相关的具体调用处理者。
Subject1,这里必须是接口
public interface Subject1 {
void request();
}
RealSubject1
public class RealSubject1 implements Subject1 {
@Override
public void request() {
System.out.println("RealSubject1");
}
}
ProxyHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler {
private Object object;
public ProxyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeInvoke();
Object result = method.invoke(object, args);
alterInvoke();
return result;
}
private void beforeInvoke() {
System.out.println("beforeInvoke");
}
private void alterInvoke() {
System.out.println("beforeInvoke");
}
}
Client
public class Client {
public static void main(String[] args) {
Subject1 subject = new RealSubject1();
InvocationHandler handler = new ProxyHandler(subject);
Subject1 proxySubject = (Subject1) Proxy.newProxyInstance(subject.getClass().getClassLoader()
, subject.getClass().getInterfaces(), handler);
proxySubject.request();
}
}
优/缺点与适用环境
- 优点
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有更好的灵活性和可扩展性。
- 缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理模式。
- 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理模式。
- 适用环境
- 当客户端对象需要访问远程主机中的对象时可以使用远程代理模式。
- 当需要一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以调用虚拟代理,例如一个对象需要很长时间才能完成加载时。
- 当需要为某一个被平凡访问的操作结果提供一个临时的存储空间,以提供多个客户端共享访问这些结果时可以使用缓冲代理。
- 当需要控制对一个对象的访问为不同用户提供不同级别的访问权限可以使用保护代理。
- 当需要为一个对象的访问(引用)提供一个额外的操作时可以使用智能引用代理。