乐趣区

关于java:腾讯大佬总结的代码重构原则看完再也不怕面试官问啦

前言

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

重构

咱们先重构一下之前的 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 {// 代码未改变...}

// 改变二:增加新的 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);

}

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

重点回顾

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

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

退出移动版