22种设计模式——代理模式

1. 概述

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。在这里插入图片描述
代理模式,相信大家都不陌生啦,反射和spring里面就是用到了代理模式,不管你学没学过,大家再来深入了解一下代理模式。

老惯例从问题引入。先来看下代理模式的概念:为某些对象提供一种代理以控制对这个对象的访问。为什么要控制某些的对象的访问呢?举个例子: 有这样一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它, 并非总是需要。该怎么解决呢?

很简单,你可能想到了懒加载(延迟加载),在实际有需要时再创建该对象。 但是这样存在一个问题:这很可能会带来很多重复代码,因为客户端所有使用到这个对象的地方都要实现延迟加载。

那你可能会说,那我把延迟加载不放在客户端,反正巨型对象的代码里面,那我客户端就不必写重复代码了。但问题又来了,这样确实可以解决重复代码的问题,但比如类可能是第三方封闭库的一部分,我们就不能把延迟加载弄到里面去了。(当然,你直接改源码的话当我没说)

代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。 代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。

简单说,就是我们既然不能直接修改巨型对象,那我们就新建一个东西,它具有我们想要增强的功能(上面的例子中指延迟加载功能),同时它也能调用巨型对象进行工作,就是在原来的基础上加上了一层。

比如下图,我们把数据库看成一个巨型对象,客户端想要实现延迟加载,但是又不能直接改数据库的代码。所以我们新建了一个代理对象,它能调用数据库的功能,同时我们也把延迟加载的代码放到代理对象里面去,以后客户端与代理对象交互就可,这样就同时实现了数据库的功能和延迟加载功能。

在这里插入图片描述
如果上面延迟加载的例子还不足以让你理解,再举一个例子,假如现在有某个需求需要强化数据库的某一个功能,OK那就又回到上面的情况,要么写在客户端造成重复代码,要么改数据库的代码,这次你很强硬哈,你就要改源码,okk那可以。

这时又来了一个新需求,但是你发现它做不出来,因为你改了源码,有一些东西被你改掉了,所以你此时你该怎么办?代理模式来帮你,新建一个代理对象,把要增强的功能写在代理对象里,这样就解决了。这个就是代理模式,应该可以理解了吧,还算是蛮简单的。

  • 总结
    1. 代理模式新增了一个代理对象,用于控制被代理对象的访问,同时也能增强被代理的对象的某些功能

2. 特点

  • 优点
    1. 你可以在客户端毫无察觉的情况下控制服务对象。
    2. 如果客户端对服务对象的生命周期没有特殊要求, 你可以对生命周期进行管理。
    3. 即使服务对象还未准备好或不存在, 代理也可以正常工作。
    4. 开闭原则。 你可以在不对服务或客户端做出修改的情况下创建新代理。
  • 缺点
    1. 代码可能会变得复杂, 因为需要新建许多类。
    2. 服务响应可能会延迟。
  • 使用场景
    1. 延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。

    2. 访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。代理可仅在客户端凭据满足要求时将请求传递给服务对象。

    3. 本地执行远程服务 (远程代理)。 适用于服务对象位于远程服务器上的情形。在这种情形中, 代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。

    4. 记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。 代理可以在向服务传递请求前进行记录。

    5. 缓存请求结果 (缓存代理)。 适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。代理可对重复请求所需的相同结果进行缓存, 还可使用请求参数作为索引缓存的键值。

    6. 智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。

      代理会将所有获取了指向服务对象或其结果的客户端记录在案。 代理会时不时地遍历各个客户端, 检查它们是否仍在运行。 如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。代理还可以记录客户端是否修改了服务对象。 其他客户端还可以复用未修改的对象。


3. 实现

3.1 静态代理

  • UML类图
    在这里插入图片描述

  • 角色说明

    1. 被代理接口:被代理的接口,定义了要增强的功能,当然这里也可以是抽象类,那代理对象就要继承这个类
    2. 被代理实现类:被代理的具体对象,实现了具体逻辑
    3. 代理对象:实现了被代理接口或者继承被代理抽象类,需要内聚一个被代理接口或者被代理抽象类,重写方法,对它们进行增强
    4. 客户端:调用代理模式的角色
  • Java代码

    1. 被代理接口
      /**
       * @Author: chy
       * @Description: 被代理接口,数据库接口
       * @Date: Create in 13:59 2021/3/17
       */
      public interface IDatabase {
          void use();
      }
      
    2. 被代理实现类
      /**
       * @Author: chy
       * @Description: 被代理对象,数据库接口实现类
       * @Date: Create in 14:00 2021/3/17
       */
      public class Database implements IDatabase{
          @Override
          public void use() {
              System.out.println("数据库正在使用。。。。。");
          }
      }
      
    3. 代理对象
      /**
       * @Author: chy
       * @Description: 代理类
       * @Date: Create in 14:01 2021/3/17
       */
      public class Proxy implements IDatabase{
          private IDatabase database;
      
          public Proxy(IDatabase database) {
              this.database = database;
          }
      
          @Override
          public void use() {
              System.out.println("延迟加载。。。。");
              database.use();
          }
      }
      
    4. 客户端
      /**
       * @Author: chy
       * @Description: 客户端
       * @Date: Create in 13:59 2021/3/17
       */
      public class Client {
          public static void main(String[] args) {
              Proxy proxy = new Proxy(new Database());
              proxy.use();
          }
      }
      
    5. 结果
      在这里插入图片描述

3.2 动态代理

动态代理和静态代理其实差不多,原理都是一样的,不一样的点就是静态代理是需要自己新建一个代理对象的,如上面例子里的Proxy类,动态代理不用自己新建代理对象,而是交给反射机制来帮我们新建。

有两种:JDK代理和Cglib代理,jdk就是Java提供给我们的,cglib代理则需要我们自己导入jar包,jdk代理是基于接口的,cglib代理是基于类的,它们的角色和别的操作都和静态代理一样,就代理对象使用方法不同。具体这里就不贴出代码了,可以去查查它们的具体用法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值