1
头图

Preface

The last one opening and closing principles most useful code changes are based on "modification" to achieve new functions. If we follow the principle of opening and closing, that is, "open to expansion and closed to modification." How to achieve the same function by "extending"?

Refactor

Let's refactor the previous Alert code to make it more extensible. The content of the reconstruction mainly consists of two parts:

  1. The first part is to encapsulate multiple input parameters of the check () function into the ApiStatInfo class;
  2. The second part is to introduce the concept of handlers, and disperse the if judgment logic in each handler.

The specific code implementation is as follows:

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, "...");

    }

  }

}

The above code is a reconstruction of Alert. Let's take a look again. How to use Alert after reconstruction? I also wrote the specific code here.

Among them, ApplicationContext is a singleton class, responsible for the creation, assembly (dependency injection of alertRule and notification), and initialization (adding handlers) of Alert.


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);

  }

}
Now, let's take a look again. Based on the refactored code, if the new function mentioned above is added, the number of interface timeout requests per second exceeds a certain maximum threshold and an alarm will be given. How can we change the code? The main changes are as follows.
  1. The first change is to add a new property timeoutCount to the ApiStatInfo class.
  2. The second change is to add a new TimeoutAlertHander class.
  3. The third change is to register a new timeoutAlertHandler in the alert object in the initializeBeans () method of the ApplicationContext class.
  4. The fourth change is: When using the Alert class, you need to set the value of timeoutCount to the input parameter apiStatInfo object of the check () function.

The changed code is as follows:


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 {// 代码未改动...}

// 改动二:添加新的 handler

public 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);

}

The refactored code is more flexible and easy to extend. If we want to add new alarm logic, we only need to create a new handler class based on the extension, without changing the logic of the original check () function. Moreover, we only need to add unit tests for the new handler class, and the old unit tests will not fail and do not need to be modified.

Key review

How to understand "open to extension, closed to modification"?

Adding a new function should be done by extending the code based on the existing code (adding modules, classes, methods, attributes, etc.) instead of modifying the existing code (modifying modules, classes, methods, attributes, etc.) carry out. Regarding the definition, we have two points to note. The first point is that the principle of opening and closing does not mean that modifications are completely eliminated, but to complete the development of new features at the minimum cost of modifying the code. The second point is that the same code change may be regarded as "modification" under coarse code granularity, and may be regarded as "extension" under fine code granularity.
More java original reading: https://javawu.com

大盛玩java
24 声望5 粉丝