设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

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

适用到其他场景
异常处理
灵活替换类对象
分批统计数据
异步执行采集和存储
使用示例
提供封装了默认依赖的构造函数
通过参数传递解决强依赖问题
抽离trimTimeFieldsToZeroOfNextDay()函数
重构EmailReporter类
解决ConsoleReporter和EmailReporter代码重复问题
重构ScheduledReporter类
ConsoleReporter的startRepeatedReport()函数问题及解决方法
学会编程技巧和设计思路
逐步优化的方法
迭代过程
通用性
容错性
扩展性
性能
易用性
自定义显示格式
自定义显示终端
完善被动触发统计的方式
提高可测试性
继承解决代码重复问题
课堂讨论
重点回顾
非功能需求完善
功能需求完善
代码重构优化
性能计数器项目

该思维导图由 AI 生成,仅供参考

上一节课中,我们针对版本 1 存在的问题(特别是 Aggregator 类、ConsoleReporter 和 EmailReporter 类)进行了重构优化。经过重构之后,代码结构更加清晰、合理、有逻辑性。不过,在细节方面还是存在一些问题,比如 ConsoleReporter、EmailReporter 类仍然存在代码重复、可测试性差的问题。今天,我们就在版本 3 中持续重构这部分代码。
除此之外,在版本 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 代码如下所示:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了如何通过设计原则和思想来完善性能计数器项目。首先,通过代码重构优化,解决了ConsoleReporter和EmailReporter中的代码重复问题,并提高了可测试性。重点讨论了提高EmailReporter的可测试性,通过抽离代码和参数传递来解决时间计算逻辑的问题。文章还详细介绍了功能需求的完善,包括接口统计信息、统计信息显示格式和终端、统计触发方式、统计时间区间和间隔等。最后,提出了版本3中还未实现的功能点,包括被动触发统计的方式、自定义显示终端和显示格式的设计。整体来说,本文通过具体的代码示例和思路分析,展示了如何通过设计原则和思想来不断完善性能计数器项目,为读者提供了一种深入理解和应用设计原则的思路。文章还涉及了性能、扩展性、容错性和通用性等方面的讨论,为读者提供了全面的技术视角。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(46)

  • 最新
  • 精选
  • 李小四
    设计模式_40: # 作业 导致多个线程的重复统计。 办法:加入进程内的全局变量(注意多线程同步问题)。 # 感想 我个人是Android工程师,客户端的开发默认就要思考一个问题:方法重复调用时(如多次点击某个按钮等)逻辑是否还正常。 25,26,39,40这四节课边听边读反复了好几遍了,因为目的是掌握该掌握的东西,而不是简单地打个卡,所以整个春节期间就卡在这四节课的重复中了,不停地循环听。。。 也有好处:我反倒对这几节内容非常熟悉了,有两点感受较深: 1> 方法论:分清楚 *功能性需求* 与 *非功能型需求* 之前是想到什么注意什么,往往做不到穷举。 2> 一步一步地重构,其实解决的是自信问题: 做事要先解决思想问题,也就是心理问题: - 没有人能够一步到位地完美解决问题,优秀的代码是演进的,也就是说,代码结构不完美的状态是跳不过去的。 - 我们始终聚焦在解决问题上,代码有问题非常正常。 - 我们要带着成就感不断重构代码,而不是带着对自己否定的愧疚感,这非常重要。 - 成就感让你追求卓越,愧疚感只是让你不想犯错(而这是做不到的)。
    2020-02-22
    2
    87
  • 辣么大
    思考题:startRepeatedReport()多次调用,会启动多个线程,每个线程都会执行统计和输出工作。 想了一种简单的实现方式,将runnable做为成员变量,第一次调用startRepeatedReport()时初始化,若多次调用,判空,返回。 public void startRepeatedReport(long periodInSeconds, long durationInSeconds) { if (runnable != null) { System.out.println("duplicate calls!"); return; } runnable = () -> { long durationInMillis = durationInSeconds * 1000; long endTimeInMillis = System.currentTimeMillis(); long startTimeInMillis = endTimeInMillis - durationInMillis; doReport(startTimeInMillis, endTimeInMillis); }; executor.scheduleAtFixedRate(runnable, 0, periodInSeconds, TimeUnit.SECONDS); } 代码放在了:https://github.com/gdhucoder/Algorithms4/tree/master/designpattern/u40
    2020-02-03
    5
    19
  • Andy
    老师能提供课程代码吗?
    2020-02-04
    16
  • 平风造雨
    调用多次可以通过多线程共享的状态变量来解决,CAS或者加锁进行状态的变更。
    2020-02-03
    14
  • 6点无痛早起学习的和尚
    这两篇实战对自己的感觉就是越来越懵,可能还需要多读读,也不知道是不是跟自己的项目做的很少有原因,前面的理论都还能消化
    2020-03-04
    1
    7
  • undefined
    深入浅出,过瘾。
    2020-02-03
    7
  • Jxin
    先回答问题: 1.会导致多余线程做多余的统计和展示。因为每次调用都会起一个异步线程输出统计数据到控制台。这样既会带来额外的性能开销,又会导致统计信息不易阅读。 2.在ConsoleReporter内部维护一个可视字段 started。然后在方法执行时,优先判断该字段是否已经变为true。如果是则不再往下执行。也算是保证该函数的幂等性。 个人疑问: 1.怎么做到这样分步展示重构过程的?我现在写,基本一边写就一边重构,停手也就差不多到合适的质量了。刻意要展示重构手法,展示的知识点会有很多疏漏,并无法做到这样一步一步的展示(下意识一步到位,并不知道怎么退到不好的代码结构)。 2.能理解栏主尽量不依赖任何框架的初衷。但对于java,spring其实才是标准,感觉是不是基于spring框架来写demo还好点? 我现在比较喜欢让代码依赖spring框架来实现,感觉这样会显得优雅一些。栏主怎么看?
    2020-02-03
    6
    7
  • 高源
    老师39,40课完整源代码可以提供下吗,我准备好好研究学习下
    2020-02-03
    3
    7
  • Q罗
    终于进去到期待已久的设计模式了
    2020-03-18
    4
  • javaadu
    课堂讨论,使用一个标记flag作为该函数被调用国的标记,并给这个函数加锁,解决并发问题
    2020-02-03
    4
收起评论
显示
设置
留言
46
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部