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

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

课堂讨论
面向对象设计和实现要做的事情
面向对象设计中的最后一步
EmailReporter
ConsoleReporter
Aggregator
EmailReporter
ConsoleReporter
EmailViewer
ConsoleViewer
Aggregator
EmailReporter
ConsoleReporter
Aggregator
RedisMetricsStorage
MetricsStorage
MetricsCollector
重点回顾
Review版本2的设计与实现
针对版本1的问题进行重构
回顾版本1的设计与实现
运用学过的设计原则和思想改进性能计数器项目(上)
参考文章

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

在第 25 节、第 26 节中,我们讲了如何对一个性能计数器框架进行分析、设计与实现,并且实践了之前学过的一些设计原则和设计思想。当时我们提到,小步快跑、逐步迭代是一种非常实用的开发模式。所以,针对这个框架的开发,我们分多个版本来逐步完善。
在第 25、26 节课中,我们实现了框架的第一个版本,它只包含最基本的一些功能,在设计与实现上还有很多不足。所以,接下来,我会针对这些不足,继续迭代开发两个版本:版本 2 和版本 3,分别对应第 39 节和第 40 节的内容。
在版本 2 中,我们会利用之前学过的重构方法,对版本 1 的设计与实现进行重构,解决版本 1 存在的设计问题,让它满足之前学过的设计原则、思想、编程规范。在版本 3 中,我们再对版本 2 进行迭代,并且完善框架的功能和非功能需求,让其满足第 25 节课中罗列的所有需求。
话不多说,让我们正式开始版本 2 的设计与实现吧!

回顾版本 1 的设计与实现

首先,让我们一块回顾一下版本 1 的设计与实现。当然,如果时间充足,你最好能再重新看一下第 25、26 节的内容。在版本 1 中,整个框架的代码被划分为下面这几个类。
MetricsCollector:负责打点采集原始数据,包括记录每次接口请求的响应时间和请求时间戳,并调用 MetricsStorage 提供的接口来存储这些原始数据。
MetricsStorage 和 RedisMetricsStorage:负责原始数据的存储和读取。
Aggregator:是一个工具类,负责各种统计数据的计算,比如响应时间的最大值、最小值、平均值、百分位值、接口访问次数、tps。
ConsoleReporter 和 EmailReporter:相当于一个上帝类(God Class),定时根据给定的时间区间,从数据库中取出数据,借助 Aggregator 类完成统计工作,并将统计结果输出到相应的终端,比如命令行、邮件。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了如何通过重构和设计原则来改进性能计数器项目。作者首先回顾了项目的第一个版本的设计与实现,指出了Aggregator类和ConsoleReporter、EmailReporter类存在的问题。在版本2中,作者计划利用重构方法对版本1的设计与实现进行改进,解决存在的设计问题,让其满足设计原则、思想和编程规范。具体来说,对Aggregator类的重构解决了代码量增加、可读性、可维护性变差的问题;对ConsoleReporter和EmailReporter类的重构解决了代码重复、职责不够单一、可测试性有待提高的问题。通过对这些类的重构,作者逐步完善了框架的功能和非功能需求,使其满足之前罗列的所有需求。整体而言,本文以项目的迭代开发为线索,深入讲解了如何运用设计原则和思想改进性能计数器项目,对于想要提升项目设计与实现水平的开发者具有一定的参考价值。

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

全部留言(46)

  • 最新
  • 精选
  • 辣么大
    问题1,reporter可测性差的问题,可以mock storage,将request信息到map中。 // mock MetricsStorage storage = new MockRedisMetricsStorage(); 问题2,reporter的创建过程可以使用简单工厂方法。Aggregator完全没有必要暴露出来,可以隐藏。 ConsoleReporter consoleReporter = ReporterFactory.createConsoleReporter(storage); 争哥的代码我复制下来,并且跟着重构了一下,想跑跑看的同学请参考: https://github.com/gdhucoder/Algorithms4/tree/master/designpattern/u39
    2020-02-01
    55
  • javaadu
    1. 看了下,ConoleReporter和EmailReporter的核心区别在于使用的显示器不同,另外就是调度的频次不同,第二个不同是可以通用化的,可以提取出一个抽象的调度器(把查询数据、调用聚合统计对象的代码都放进去),支持每秒、分、时、天调度;ConsoleReportor和EmailReporter都使用这个调度器,自己只维护对应的显示器对象的引用就可以了。
    2020-01-31
    26
  • 小晏子
    课后思考: 1. 将两个reporter中的run里的逻辑单独提取出来做成一个公共函数void doReport(duration, endTime, startTime),这个函数易于单独测试,两个reporter类中调用doReport,因为两个reporter类中并无特殊的逻辑处理,只使用了jdk本身提供的功能,我们可以相信jdk本身的正确性,所以这块就可以不写单元测试了,这就简化了测试也解决了重复代码的问题。
    2020-01-31
    19
  • javaadu
    2. 如果使用Spring Boot之类的框架,就可以利用框架做自动注入;如果没有,则可以用工厂方法设计模式来拼比掉复杂的对象创建过程
    2020-01-31
    16
  • 守拙
    课堂讨论 1. 今天我们提到,重构之后的 ConsoleReporter 和 EmailReporter 仍然存在代码重复和可测试性差的问题,你可以思考一下,应该如何解决呢? ConsoleReporter和EmailReporter的代码重复集中在viewer#output()部分.可以抽象一个AbsReporter,将重复代码放在基类中,并让ConsoleReporter和EmailReporter继承自AbsReporter. 这里基类与衍生类完全符合is-a关系, 但并未使用多态性. 2. 从上面的使用示例中,我们可以看出,框架易用性有待提高:ConsoleReporter 和 EmailReporter 的创建过程比较复杂,使用者需要正确地组装各种类才行。对于框架的易用性,你有没有什么办法改善一下呢? ​ 可以使用builder模式改造, 提供更友好的依赖注入方式. 除此以外, 还应编写良好的注释, 帮助客户端程序员正确的使用框架. ​ 示例: ​ ConsoleReporter instance = ConsoleReporter.Builder() ​ .setMetricsStorate(storage) ​ .setAggregator(aggregator) ​ .setStatViewer(viewer) ​ .setExecutor(executor) ​ .build();
    2020-01-31
    14
  • Jessica
    这句话太赞同了:面向对象设计和实现要做的事情,就是把合适的代码放到合适的类中。当我们要实现某个功能的时候,不管如何设计,所需要编写的代码量基本上是一样的,唯一的区别就是如何将这些代码划分到不同的类中。不同的人有不同的划分方法,对应得到的代码结构(比如类与类之间交互等)也不尽相同。
    2020-03-17
    10
  • Jxin
    1.将定时和输出报表这两件事分离。单独的定时线程,在关键的时间点都触发一个事件。输出报表的两个类去监听自己关心的时间job的事件(生产消费模式)。如此一来,定时触发好不好使不再是我api使用方考虑的事。我只需要测试对应输出报表的业务是否正常。然后就控制台和邮件这两个报表类,其生成报表的逻辑是一样的,仅仅是展示的“方式”不一样。所以让我选,我会合并这两个类,生成报表的逻辑为私有方法,然后单独写一个控制台输出的public方法和邮箱输出的public方法(输出模式多,且存在组合需求的话会采用分发+约定的方式,降低调用方负担)。那么这个类,生成报表逻辑公用,两个输出方法是走的api,所以也不关心,我只需要测试 报表生成的逻辑即可。 2.越灵活自然越复杂。 约定大于配置呗。除了业务埋点必须实现,其他都可以约定。
    2020-01-31
    7
  • 时熵
    Aggregator重构后时间复杂度感觉上升了好多
    2021-07-02
    3
    5
  • 红豆成香
    老师,大家好,有个问题一直困扰着我,看到老师这里也存在,希望老师和同学们帮我解惑:max和min等拆分成独立的函数之前,一个for循环就可以同时完成计算;拆分之后,每个独立的方法都要循环一次,如果数据很大,这样就很耗时间,而且我总觉得一个for循环能解决多个问题特别的爽,有一种一石二鸟的感觉,但是拆分之后代码逻辑更加清晰,我不知道要怎么取舍了
    2020-11-10
    5
    5
  • liu_liu
    1. 可定义父类,重复代码抽取为函数进行复用 2. 用工厂方法,屏蔽创建过程
    2020-01-31
    5
收起评论
显示
设置
留言
46
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部