代理模式(Proxy Pattern)是一种常见的设计模式,在软件开发中有着广泛的应用。其核心思想是通过创建一个代理类来控制对另一个对象的访问,从而实现对目标对象功能的扩展、保护或其他附加操作。
一、核心思想
代理模式的核心思想在于提供一个代理对象,作为客户端与目标对象之间的中介。客户端并不直接调用目标对象的方法,而是通过调用代理对象的方法来间接调用目标对象的方法。这样可以在调用目标对象方法之前或之后添加额外的逻辑,如权限检查、日志记录、事务处理等。
二、定义与结构
定义:代理模式为其他对象提供一种代理以控制对这个对象的访问。
结构:
- 抽象角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色(Real Subject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
角色
在代理模式中,通常涉及以下三个角色:
- 抽象主题(Subject):定义了真实主题和代理主题的共同接口。
- 真实主题(Real Subject):实现了具体的业务逻辑,是代理模式中被代理的对象。
- 代理主题(Proxy Subject):持有对真实主题的引用,可以在调用真实主题之前或之后执行附加的操作。
三、实现步骤及代码示例
以下是通过Java技术框架实现代理模式的详细步骤及代码示例:
静态代理:
- 定义抽象主题接口:
java
public interface Subject {
void request();
}
- 实现真实主题类:
java
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
- 实现代理主题类:
java
public class ProxySubject implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("ProxySubject: Preparing for request.");
}
private void postRequest() {
System.out.println("ProxySubject: Finishing up request.");
}
}
- 客户端代码:
java
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.request();
}
}
动态代理:
- 定义抽象主题接口(同上)。
- 实现真实主题类(同上)。
- 创建InvocationHandler实现类:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler {
private final Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preRequest();
Object result = method.invoke(target, args);
postRequest();
return result;
}
private void preRequest() {
System.out.println("ProxySubject: Preparing for request.");
}
private void postRequest() {
System.out.println("ProxySubject: Finishing up request.");
}
}
- 客户端代码:
java
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new ProxyHandler(realSubject);
Subject subject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);
subject.request();
}
}
四、常见技术框架应用
1、网络请求代理服务器
在网络请求中,可能会使用代理服务器。假设我们有一个简单的网络请求接口和实现:
- 定义抽象主题(网络请求接口):
java
interface NetworkRequest {
String request(String url);
}
- 真实主题(实际的网络请求实现):
java
class RealNetworkRequest implements NetworkRequest {
@Override
public String request(String url) {
System.out.println("Sending request to " + url);
// 这里可以是实际的网络请求代码,比如使用HttpURLConnection等
return "Response from " + url;
}
}
- 代理(带有缓存功能的网络请求代理):
java
import java.util.HashMap;
import java.util.Map;
class CachedNetworkRequestProxy implements NetworkRequest {
private RealNetworkRequest realRequest;
private Map<String, String> cache = new HashMap<>();
@Override
public String request(String url) {
if (cache.containsKey(url)) {
System.out.println("Retrieving from cache for " + url);
return cache.get(url);
} else {
if (realRequest == null) {
realRequest = new RealNetworkRequest();
}
String response = realRequest.request(url);
cache.put(url, response);
return response;
}
}
}
- 使用示例:
java
NetworkRequest request = new CachedNetworkRequestProxy();
System.out.println(request.request("https://example.com"));
System.out.println(request.request("https://example.com"));
这里代理对象CachedNetworkRequestProxy
在第一次请求时会调用真实对象RealNetworkRequest
进行网络请求,然后将结果缓存起来。之后相同的请求就直接从缓存中获取,减少了网络请求的次数。
2、Spring AOP
在Java中,代理模式也很常见,特别是在Spring框架中。以下是一个简单的Spring AOP(面向切面编程)示例:
java
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
// Service interface
public interface SimpleService {
void perform();
}
// Service implementation
public class SimpleServiceImpl implements SimpleService {
@Override
public void perform() {
System.out.println("Performing service operation");
}
}
// Before advice
public class LoggingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("Logging before method: " + method.getName());
}
}
// Client code
public class ProxyDemo {
public static void main(String[] args) {
SimpleService service = new SimpleServiceImpl();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(service);
factory.addAdvice(new LoggingBeforeAdvice());
SimpleService proxy = (SimpleService) factory.getProxy();
proxy.perform(); // Output: Logging before method: perform \n Performing service operation
}
}
在这个例子中,ProxyFactory
创建了一个代理对象,并在方法调用之前添加了日志记录功能。
在Java中,代理模式通常用于远程方法调用(RMI)、JDK动态代理或CGLIB代理。在.NET中,可以用Castle DynamicProxy库来创建代理。而在Python中,wrapt库提供了强大的装饰器和代理功能。在C#中,可以使用RealProxy类来实现动态代理。这些技术框架中的代理模式实现方式虽然有所不同,但核心思想都是相同的,即通过代理对象来控制对目标对象的访问。
五、应用场景
代理模式的应用场景非常广泛,包括但不限于以下几个方面:
- 远程代理:为其他对象提供一种代理以控制对这个对象的远程访问。例如,在分布式系统中,客户端可以通过代理对象来访问远程服务器上的对象。
- 虚拟代理:根据需要创建开销很大的对象。在真实对象创建之前,虚拟代理对象可以充当其代理。例如,在加载大型图片或视频时,可以先显示一个占位符或缩略图,待图片或视频加载完成后再显示真实内容。
- 安全代理:控制对资源的访问权限。例如,在Web应用中,可以通过代理对象来检查用户是否具有访问某个资源的权限。
- 智能代理:当调用真实对象时,代理对象可以附加一些额外的处理逻辑。例如,在调用数据库查询方法之前,代理对象可以自动添加分页、排序等处理逻辑。
六、优缺点
优点:
- 中介作用:代理对象可以在客户端和目标对象之间起到中介的作用,从而保护目标对象。
- 功能扩展:代理对象可以扩展目标对象的功能,而不需要修改目标对象的代码。
- 降低耦合度:代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性。
缺点:
- 类数量增加:代理模式会造成系统设计中类的数量增加,从而增加了系统的复杂度。
- 请求处理变慢:在客户端和目标对象之间增加一个代理对象,可能会造成请求处理速度变慢。
综上所述,代理模式是一种非常有用的设计模式,它可以在不修改目标对象代码的前提下,实现对目标对象功能的扩展和保护。同时,代理模式也具有一定的缺点,需要在具体应用场景中进行权衡和选择。