前言

上一篇开闭准则最有用的代码改变是基于 “批改” 的形式来实现新性能的。如果咱们遵循开闭准则,也就是 “对扩大凋谢、对批改敞开”。那如何通过 “扩大” 的形式,来实现同样的性能呢?

重构

咱们先重构一下之前的 Alert 代码,让它的扩展性更好一些。重构的内容次要蕴含两局部:

  1. 第一局部是将 check () 函数的多个入参封装成 ApiStatInfo 类;
  2. 第二局部是引入 handler 的概念,将 if 判断逻辑扩散在各个 handler 中。

具体的代码实现如下所示:

public class Alert {  private List<AlertHandler> alertHandlers = new ArrayList<>();  public void addAlertHandler(AlertHandler alertHandler) {    this.alertHandlers.add(alertHandler);  }  public void check(ApiStatInfo apiStatInfo) {    for (AlertHandler handler : alertHandlers) {      handler.check(apiStatInfo);    }  }}public class ApiStatInfo {// 省略 constructor/getter/setter 办法  private String api;  private long requestCount;  private long errorCount;  private long durationOfSeconds;}public abstract class AlertHandler {  protected AlertRule rule;  protected Notification notification;  public AlertHandler(AlertRule rule, Notification notification) {    this.rule = rule;    this.notification = notification;  }  public abstract void check(ApiStatInfo apiStatInfo);}public class TpsAlertHandler extends AlertHandler {  public TpsAlertHandler(AlertRule rule, Notification notification) {    super(rule, notification);  }  @Override  public void check(ApiStatInfo apiStatInfo) {    long tps = apiStatInfo.getRequestCount()/ apiStatInfo.getDurationOfSeconds();    if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()) {      notification.notify(NotificationEmergencyLevel.URGENCY, "...");    }  }}public class ErrorAlertHandler extends AlertHandler {  public ErrorAlertHandler(AlertRule rule, Notification notification){    super(rule, notification);  }  @Override  public void check(ApiStatInfo apiStatInfo) {    if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).getMaxErrorCount()) {      notification.notify(NotificationEmergencyLevel.SEVERE, "...");    }  }}

下面的代码是对 Alert 的重构,咱们再来看下,重构之后的 Alert 该如何应用呢?具体的应用代码我也写在这里了。

其中,ApplicationContext 是一个单例类,负责 Alert 的创立、组装(alertRule 和 notification 的依赖注入)、初始化(增加 handlers)工作。

public class ApplicationContext {  private AlertRule alertRule;  private Notification notification;  private Alert alert;  public void initializeBeans() {    alertRule = new AlertRule(/*. 省略参数.*/); // 省略一些初始化代码    notification = new Notification(/*. 省略参数.*/); // 省略一些初始化代码    alert = new Alert();    alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));    alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));  }  public Alert getAlert() { return alert; }  // 饿汉式单例  private static final ApplicationContext instance = new ApplicationContext();  private ApplicationContext() {    instance.initializeBeans();  }  public static ApplicationContext getInstance() {    return instance;  }}public class Demo {  public static void main(String[] args) {    ApiStatInfo apiStatInfo = new ApiStatInfo();    //... 省略设置 apiStatInfo 数据值的代码    ApplicationContext.getInstance().getAlert().check(apiStatInfo);  }}
当初,咱们再来看下,基于重构之后的代码,如果再增加下面讲到的那个新性能,每秒钟接口超时申请个数超过某个最大阈值就告警,咱们又该如何改变代码呢?次要的改变有上面到处。
  1. 第一处改变是:在 ApiStatInfo 类中增加新的属性 timeoutCount。
  2. 第二处改变是:增加新的 TimeoutAlertHander 类。
  3. 第三处改变是:在 ApplicationContext 类的 initializeBeans () 办法中,往 alert 对象中注册新的 timeoutAlertHandler。
  4. 第四处改变是:在应用 Alert 类的时候,须要给 check () 函数的入参 apiStatInfo 对象设置 timeoutCount 的值。

改变之后的代码如下所示:

public class Alert { // 代码未改变... }public class ApiStatInfo {// 省略 constructor/getter/setter 办法  private String api;  private long requestCount;  private long errorCount;  private long durationOfSeconds;  private long timeoutCount; // 改变一:增加新字段}public abstract class AlertHandler { // 代码未改变... }public class TpsAlertHandler extends AlertHandler {// 代码未改变...}public class ErrorAlertHandler extends AlertHandler {// 代码未改变...}// 改变二:增加新的 handlerpublic class TimeoutAlertHandler extends AlertHandler {// 省略代码...}public class ApplicationContext {  private AlertRule alertRule;  private Notification notification;  private Alert alert;  public void initializeBeans() {    alertRule = new AlertRule(/*. 省略参数.*/); // 省略一些初始化代码    notification = new Notification(/*. 省略参数.*/); // 省略一些初始化代码    alert = new Alert();    alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));    alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));    // 改变三:注册 handler    alert.addAlertHandler(new TimeoutAlertHandler(alertRule, notification));  }  //... 省略其余未改变代码...}public class Demo {  public static void main(String[] args) {    ApiStatInfo apiStatInfo = new ApiStatInfo();    //... 省略 apiStatInfo 的 set 字段代码    apiStatInfo.setTimeoutCount(289); // 改变四:设置 tiemoutCount 值    ApplicationContext.getInstance().getAlert().check(apiStatInfo);}

重构之后的代码更加灵便和易扩大。如果咱们要想增加新的告警逻辑,只须要基于扩大的形式创立新的 handler 类即可,不须要改变原来的 check () 函数的逻辑。而且,咱们只须要为新的 handler 类增加单元测试,老的单元测试都不会失败,也不必批改。

重点回顾

如何了解 “对扩大凋谢、对批改敞开”?

增加一个新的性能,应该是通过在已有代码根底上扩大代码(新增模块、类、办法、属性等),而非批改已有代码(批改模块、类、办法、属性等)的形式来实现。对于定义,咱们有两点要留神。第一点是,开闭准则并不是说齐全杜绝批改,而是以最小的批改代码的代价来实现新性能的开发。第二点是,同样的代码改变,在粗代码粒度下,可能被认定为 “批改”;在细代码粒度下,可能又被认定为 “扩大”。
更多java原创浏览:https://javawu.com