39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)
王争
该思维导图由 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
《设计模式之美》,新⼈⾸单¥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/u392020-02-0155
- javaadu1. 看了下,ConoleReporter和EmailReporter的核心区别在于使用的显示器不同,另外就是调度的频次不同,第二个不同是可以通用化的,可以提取出一个抽象的调度器(把查询数据、调用聚合统计对象的代码都放进去),支持每秒、分、时、天调度;ConsoleReportor和EmailReporter都使用这个调度器,自己只维护对应的显示器对象的引用就可以了。2020-01-3126
- 小晏子课后思考: 1. 将两个reporter中的run里的逻辑单独提取出来做成一个公共函数void doReport(duration, endTime, startTime),这个函数易于单独测试,两个reporter类中调用doReport,因为两个reporter类中并无特殊的逻辑处理,只使用了jdk本身提供的功能,我们可以相信jdk本身的正确性,所以这块就可以不写单元测试了,这就简化了测试也解决了重复代码的问题。2020-01-3119
- javaadu2. 如果使用Spring Boot之类的框架,就可以利用框架做自动注入;如果没有,则可以用工厂方法设计模式来拼比掉复杂的对象创建过程2020-01-3116
- 守拙课堂讨论 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-3114
- Jessica这句话太赞同了:面向对象设计和实现要做的事情,就是把合适的代码放到合适的类中。当我们要实现某个功能的时候,不管如何设计,所需要编写的代码量基本上是一样的,唯一的区别就是如何将这些代码划分到不同的类中。不同的人有不同的划分方法,对应得到的代码结构(比如类与类之间交互等)也不尽相同。2020-03-1710
- Jxin1.将定时和输出报表这两件事分离。单独的定时线程,在关键的时间点都触发一个事件。输出报表的两个类去监听自己关心的时间job的事件(生产消费模式)。如此一来,定时触发好不好使不再是我api使用方考虑的事。我只需要测试对应输出报表的业务是否正常。然后就控制台和邮件这两个报表类,其生成报表的逻辑是一样的,仅仅是展示的“方式”不一样。所以让我选,我会合并这两个类,生成报表的逻辑为私有方法,然后单独写一个控制台输出的public方法和邮箱输出的public方法(输出模式多,且存在组合需求的话会采用分发+约定的方式,降低调用方负担)。那么这个类,生成报表逻辑公用,两个输出方法是走的api,所以也不关心,我只需要测试 报表生成的逻辑即可。 2.越灵活自然越复杂。 约定大于配置呗。除了业务埋点必须实现,其他都可以约定。2020-01-317
- 时熵Aggregator重构后时间复杂度感觉上升了好多2021-07-0235
- 红豆成香老师,大家好,有个问题一直困扰着我,看到老师这里也存在,希望老师和同学们帮我解惑:max和min等拆分成独立的函数之前,一个for循环就可以同时完成计算;拆分之后,每个独立的方法都要循环一次,如果数据很大,这样就很耗时间,而且我总觉得一个for循环能解决多个问题特别的爽,有一种一石二鸟的感觉,但是拆分之后代码逻辑更加清晰,我不知道要怎么取舍了2020-11-1055
- liu_liu1. 可定义父类,重复代码抽取为函数进行复用 2. 用工厂方法,屏蔽创建过程2020-01-315
收起评论