设计模式学习——代理模式

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

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的动态代理

posted @   油虾条  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示