设计模式学习——代理模式
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
1. 什么是代理
结合程序,我们一般把代理涉及的部分分为以下四个:
抽象服务:一般会使用接口或者抽象类来解决,例如服务接口,服务抽象类。
服务(Service):被代理的对象。提供了一些业务的逻辑来实现一些功能。这些服务可能是我们自己写的程序,也可能是第三方提供的类库,是对抽象服务的具体实现。
客户端(Client):调用服务的发起者,即访问代理对象的对象,最终目的是要得到服务的功能。
代理(Proxy):连接服务和客户端,客户端不是直接访问服务,而是通过代理进行服务。代理在这个过程前后可以添加自己额外的操作。
2. 代理实现
知道了代理的概念,我们尝试来实现一个简单的代理。
2.1 添加日志功能的例子
假设我们有个程序的功能是发送 Http 请求,并打印响应结果。
伪代码如下
// 功能接口
interface HttpRequest {
method sendRequest()
}
// HttpRequest 接口具体实现
class HttpURLConnectionHelper implements HttpRequest{
method sendRequest(){
··· // 请求逻辑代码
return response;
}
}
// 测试类
class App {
method main() {
···
HttpURLConnectionHelper httpURLConnectionHelper = new HttpURLConnectionHelper();
// 调用方法
httpURLConnectionHelper.sendRequest()
···
}
}
需求是在每个请求前打印请求的 url 和 请求方法,同时要求不改变实现类的代码。
思路就是添加一个代理类,然后调用代理类,这种代理方法又称为静态代理。
// http请求日志代理类,实现功能接口
class HttpRequestWithLog implements HttpRequest {
private HttpURLConnectionHelper service;
// 构造方法传入服务
constructor HttpRequestWithLog(HttpURLConnectionHelper service) {
this.service = service;
}
// 重写方法
method sendRequest() {
log(); // 打印日志
// 调用真实服务的方法
return service.sendRequest();
}
}
// 测试类
class App {
method main() {
···
HttpURLConnectionHelper httpURLConnectionHelper = new HttpURLConnectionHelper();
HttpRequestWithLog httpRequestWithLog = new HttpRequestWithLog(httpURLConnectionHelper);
// 调用代理类的方法
httpRequestWithLog.sendRequest()
···
}
}
例子完整代码
interface HttpRequest {
String sendRequest(String urlParam,String requestType);
}
// http 请求的具体实现
class HttpURLConnectionHelper implements HttpRequest{
public String sendRequest(String urlParam,String requestType) {
HttpURLConnection con = null;
BufferedReader buffer = null;
StringBuffer resultBuffer = null;
try {
URL url = new URL(urlParam);
con = (HttpURLConnection) url.openConnection();
con.setRequestMethod(requestType);
int responseCode = con.getResponseCode();
if(responseCode == HttpURLConnection.HTTP_OK){
InputStream inputStream = con.getInputStream();
resultBuffer = new StringBuffer();
String line;
buffer = new BufferedReader(new InputStreamReader(inputStream, "GBK"));
while ((line = buffer.readLine()) != null) {
resultBuffer.append(line);
}
return resultBuffer.toString();
}
}catch(Exception e) {
e.printStackTrace();
}
return "";
}
}
// http请求日志代理类
class HttpRequestWithLog implements HttpRequest {
private HttpURLConnectionHelper service;
public HttpRequestWithLog(HttpURLConnectionHelper service) {
this.service = service;
}
@Override
public String sendRequest(String urlParam, String requestType) {
this.log(urlParam, requestType);
return service.sendRequest(urlParam, requestType);
}
// 打印请求的url和请求方法
private void log(String urlParam, String requestType){
System.out.println("url:" + urlParam + "\n" +
"requestType:" + requestType);
}
}
// 测试类
class App{
public static void main(String[] args) {
String url ="http://www.baidu.com";
HttpURLConnectionHelper httpURLConnectionHelper = new HttpURLConnectionHelper();
HttpRequestWithLog httpRequestWithLog = new HttpRequestWithLog(httpURLConnectionHelper);
System.out.println(httpRequestWithLog.sendRequest(url, "GET"));
}
}
3. 代理使用场景
除了添加日志,代理还能用在很多地方。
- 延迟初始化(虚拟代理)。对于一个偶尔使用的重量级服务对象,我们希望它在实际有需要时再创建。
- 访问控制(保护代理)。有时候我们希望只有特定客户端能够使用服务对象。 可以用代理仅在客户端凭据满足要求时将请求传递给服务对象。
- 本地执行远程服务(远程代理)。当我们的服务对象位于远程服务器上的情形时,代理可以通过网络传递客户端请求,负责处理所有与网络相关的复杂细节。
- 记录日志请求(日志记录代理)。代理可以在向服务传递请求前后进行记录。
- 缓存请求结果(缓存代理)。特别是当一些请求返回结果体积很大时(例如视频数据),需要缓存客户请求结果并对缓存生命周期进行管理时,可以用代理对重复请求所需的相同结果进行缓存,还可以使用请求参数作为索引缓存的键值
- 智能引用。可在没有客户端使用某个重量级对象时立即销毁该对象。代理会将所有获取了指向服务对象或其结果的客户端记录在案。 代理会时不时地遍历各个客户端, 检查它们是否仍在运行。 如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。代理还可以记录客户端是否修改了服务对象。 其他客户端还可以复用未修改的对象。
4. 代理的优缺点
优点:
- 可以使服务代码编写更加纯粹,不需要关心公共业务逻辑
- 公共业务交给代理角色,实现了业务的分工处理。
- 当公共业务扩展时,便于集中管理。
缺点:
- 一个服务就会产生一个代理类,增加代码量,降低开发效率
解决方案:
要解决代理的缺点,我们就要让程序动态来帮助我们动态地创建类,我们把这种代理方式称为动态代理。例如,借助 Java 的反射机制可以实现动态代理。关于静态代理到动态代理的实现例子可以参考我的一篇笔记Java静态代理和基于JDK的动态代理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具