Skip to content

Latest commit

 

History

History
179 lines (128 loc) · 6 KB

《设计模式之美》之代理模式.md

File metadata and controls

179 lines (128 loc) · 6 KB

《设计模式之美》之代理模式

Proxy Design Pattern,在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。

来一段业务逻辑代码

public class UserController {
  //...省略其他属性和方法...
  private MetricsCollector metricsCollector; // 依赖注入

  public UserVo login(String telephone, String password) {
    long startTimestamp = System.currentTimeMillis();

    // ... 省略login逻辑...

    long endTimeStamp = System.currentTimeMillis();
    long responseTime = endTimeStamp - startTimestamp;
    RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);
    metricsCollector.recordRequest(requestInfo);

    //...返回UserVo数据...
  }

  public UserVo register(String telephone, String password) {
    long startTimestamp = System.currentTimeMillis();

    // ... 省略register逻辑...

    long endTimeStamp = System.currentTimeMillis();
    long responseTime = endTimeStamp - startTimestamp;
    RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);
    metricsCollector.recordRequest(requestInfo);

    //...返回UserVo数据...
  }
}
  • 上面代码中主要的两个方法login和register中,突出了通过MetricsController进行请求信息统计的逻辑
  • 这种类似日志、监控的逻辑和业务代码混到一起会产生一些问题
    • 业务代码和非业务代码混到一起,有点违反职责单一原则
    • 后面如果要替换信息统计框架时,必须改动到UserController这种业务代码的类,改动成本大

代理模式来优化

接口实现的代理模式

  • 需要新增一个代理类和一个接口,接口中列出需要被代理的方法
  • 让原始类和代理类实现该接口
  • 代理类中持有原始类对象,在代理方法中要做的是附加功能的逻辑,同时要记得通过原始类对象执行原始类的业务逻辑
public interface IUserController {
  UserVo login(String telephone, String password);
  UserVo register(String telephone, String password);
}

public class UserController implements IUserController {
  //...省略其他属性和方法...

  @Override
  public UserVo login(String telephone, String password) {
    //...省略login逻辑...
    //...返回UserVo数据...
  }

  @Override
  public UserVo register(String telephone, String password) {
    //...省略register逻辑...
    //...返回UserVo数据...
  }
}

public class UserControllerProxy implements IUserController {
  private MetricsCollector metricsCollector;
  private UserController userController;

  public UserControllerProxy(UserController userController) {
    this.userController = userController;
    this.metricsCollector = new MetricsCollector();
  }

  @Override
  public UserVo login(String telephone, String password) {
    long startTimestamp = System.currentTimeMillis();

    // 委托
    UserVo userVo = userController.login(telephone, password);

    long endTimeStamp = System.currentTimeMillis();
    long responseTime = endTimeStamp - startTimestamp;
    RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);
    metricsCollector.recordRequest(requestInfo);

    return userVo;
  }

  @Override
  public UserVo register(String telephone, String password) {
    long startTimestamp = System.currentTimeMillis();

    UserVo userVo = userController.register(telephone, password);

    long endTimeStamp = System.currentTimeMillis();
    long responseTime = endTimeStamp - startTimestamp;
    RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);
    metricsCollector.recordRequest(requestInfo);

    return userVo;
  }
}

//UserControllerProxy使用举例
//因为原始类和代理类实现相同的接口,是基于接口而非实现编程
//将UserController类对象替换为UserControllerProxy类对象,不需要改动太多代码
IUserController userController = new UserControllerProxy(new UserController());

继承实现的代理模式

对于无法修改原始类使之实现接口的情况下,也可以通过继承的方式来实现代理模式

public class UserControllerProxy extends UserController {
  private MetricsCollector metricsCollector;

  public UserControllerProxy() {
    this.metricsCollector = new MetricsCollector();
  }

  public UserVo login(String telephone, String password) {
    long startTimestamp = System.currentTimeMillis();

    UserVo userVo = super.login(telephone, password);

    long endTimeStamp = System.currentTimeMillis();
    long responseTime = endTimeStamp - startTimestamp;
    RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);
    metricsCollector.recordRequest(requestInfo);

    return userVo;
  }

  public UserVo register(String telephone, String password) {
    long startTimestamp = System.currentTimeMillis();

    UserVo userVo = super.register(telephone, password);

    long endTimeStamp = System.currentTimeMillis();
    long responseTime = endTimeStamp - startTimestamp;
    RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);
    metricsCollector.recordRequest(requestInfo);

    return userVo;
  }
}
//UserControllerProxy使用举例
UserController userController = new UserControllerProxy();

动态代理

当有些附加功能需要添加到很多类中时,开发者要挟很多重复代码,成本较大。这时可以使用动态代理(Dynamic Proxy)来解决。

  • 其实就是使用编程语言反射的特性,在应用启动时或合适的时机自动的创建、加载、设置代理类
  • 代理类的功能定义、哪些需要代理等信息可以通过配置文件的形式进行配置

代理模式的应用场景

代理模式要解决的是给原类添加附加的功能

业务系统的非功能性需求

如鉴权、限流、日志任务

缓存

后端接口可能需要一个有缓存功能的版本,如果再开发一套新的可能没太有必要,可以用动态代理实现,增加、删除、修改缓存时间等功能都能满足