运用学过的设计原则和思想完善之前讲的性能计数器项目(下)

上一节课中,我们针对版本 1 存在的问题(特别是 Aggregator 类、ConsoleReporter 和 EmailReporter 类)进行了重构优化。经过重构之后,代码结构更加清晰、合理、有逻辑性。不过,在细节方面还是存在一些问题,比如 ConsoleReporter、EmailReporter 类仍然存在代码重复、可测试性差的问题。今天,我们就在版本 3 中持续重构这部分代码。

除此之外,在版本 3 中,我们还会继续完善框架的功能和非功能需求。比如,让原始数据的采集和存储异步执行,解决聚合统计在数据量大的情况下会导致内存吃紧问题,以及提高框架的易用性等,让它成为一个能用且好用的框架。

代码重构优化

我们知道,继承能解决代码重复的问题。我们可以将 ConsoleReporter 和 EmailReporter 中的相同代码逻辑,提取到父类 ScheduledReporter 中,以解决代码重复问题。按照这个思路,重构之后的代码如下所示:


public abstract class ScheduledReporter {
  protected MetricsStorage metricsStorage;
  protected Aggregator aggregator;
  protected StatViewer viewer;

  public ScheduledReporter(MetricsStorage metricsStorage, Aggregator aggregator, StatViewer viewer) {
    this.metricsStorage = metricsStorage;
    this.aggregator = aggregator;
    this.viewer = viewer;
  }

  protected void doStatAndReport(long startTimeInMillis, long endTimeInMillis) {
    long durationInMillis = endTimeInMillis -  startTimeInMillis;
    Map<String, List<RequestInfo>> requestInfos =
            metricsStorage.getRequestInfos(startTimeInMillis, endTimeInMillis);
    Map<String, RequestStat> requestStats = aggregator.aggregate(requestInfos, durationInMillis);
    viewer.output(requestStats, startTimeInMillis, endTimeInMillis);
  }

}

ConsoleReporter 和 EmailReporter 代码重复的问题解决了,那我们再来看一下代码的可测试性问题。因为 ConsoleReporter 和 EmailReporter 的代码比较相似,且 EmailReporter 的代码更复杂些,所以,关于如何重构来提高其可测试性,我们拿 EmailReporter 来举例说明。将重复代码提取到父类 ScheduledReporter 之后,EmailReporter 代码如下所示:


public class EmailReporter extends ScheduledReporter {
  private static final Long DAY_HOURS_IN_SECONDS = 86400L;

  private MetricsStorage metricsStorage;
  private Aggregator aggregator;
  private StatViewer viewer;

  public EmailReporter(MetricsStorage metricsStorage, Aggregator aggregator, StatViewer viewer) {
    this.metricsStorage = metricsStorage;
    this.aggregator = aggregator;
    this.viewer = viewer;
  }

  public void startDailyReport() {
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DATE, 1);
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    Date firstTime = calendar.getTime();

    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
      @Override
      public void run() {
        long durationInMillis = DAY_HOURS_IN_SECONDS * 1000;
        long endTimeInMillis = System.currentTimeMillis();
        long startTimeInMillis = endTimeInMillis - durationInMillis;
        doStatAndReport(startTimeInMillis, endTimeInMillis);
      }
    }, firstTime, DAY_HOURS_IN_SECONDS * 1000);
  }
}
posted @ 2021-06-01 16:26  ukyo--君君小时候  阅读(56)  评论(0编辑  收藏  举报