设计模式之美
王争
前Google工程师,《数据结构与算法之美》专栏作者
立即订阅
19720 人已学习
课程目录
已更新 43 讲 / 共 100 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 (1讲)
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
免费
设计模式学习导读 (3讲)
01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想:面向对象 (11讲)
04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?
05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?
06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?
08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?
09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?
10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则与思想:设计原则 (12讲)
15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?
21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?
22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?
23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?
24 | 实战一(下):如何实现一个遵从设计原则的积分兑换系统?
25 | 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
26 | 实战二(下):如何实现一个支持各种统计规则的性能计数器?
设计原则与思想:规范与重构 (11讲)
27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?
28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?
29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?
30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?
31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)
32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)
33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)
34 | 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题
35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?
37 | 实战二(下):重构ID生成器项目中各函数的异常处理代码
设计原则与思想:总结课 (2讲)
38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点
39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)
不定期加餐 (3讲)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍推荐
春节特别加餐 | 王争:如何学习《设计模式之美》专栏?
设计模式之美
登录|注册

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

王争 2020-01-31
第 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《设计模式之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(9)

  • 小晏子
    课后思考:
    1. 将两个reporter中的run里的逻辑单独提取出来做成一个公共函数void doReport(duration, endTime, startTime),这个函数易于单独测试,两个reporter类中调用doReport,因为两个reporter类中并无特殊的逻辑处理,只使用了jdk本身提供的功能,我们可以相信jdk本身的正确性,所以这块就可以不写单元测试了,这就简化了测试也解决了重复代码的问题。
    2020-01-31
    2
  • javaadu
    2. 如果使用Spring Boot之类的框架,就可以利用框架做自动注入;如果没有,则可以用工厂方法设计模式来拼比掉复杂的对象创建过程
    2020-01-31
    1
  • javaadu
    1. 看了下,ConoleReporter和EmailReporter的核心区别在于使用的显示器不同,另外就是调度的频次不同,第二个不同是可以通用化的,可以提取出一个抽象的调度器(把查询数据、调用聚合统计对象的代码都放进去),支持每秒、分、时、天调度;ConsoleReportor和EmailReporter都使用这个调度器,自己只维护对应的显示器对象的引用就可以了。
    2020-01-31
    1
  • 辣么大
    问题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
  • Jxin
    1.将定时和输出报表这两件事分离。单独的定时线程,在关键的时间点都触发一个事件。输出报表的两个类去监听自己关心的时间job的事件(生产消费模式)。如此一来,定时触发好不好使不再是我api使用方考虑的事。我只需要测试对应输出报表的业务是否正常。然后就控制台和邮件这两个报表类,其生成报表的逻辑是一样的,仅仅是展示的“方式”不一样。所以让我选,我会合并这两个类,生成报表的逻辑为私有方法,然后单独写一个控制台输出的public方法和邮箱输出的public方法(输出模式多,且存在组合需求的话会采用分发+约定的方式,降低调用方负担)。那么这个类,生成报表逻辑公用,两个输出方法是走的api,所以也不关心,我只需要测试 报表生成的逻辑即可。

    2.越灵活自然越复杂。 约定大于配置呗。除了业务埋点必须实现,其他都可以约定。
    2020-01-31
  • 守拙
    课堂讨论

    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
  • liu_liu
    1. 可定义父类,重复代码抽取为函数进行复用
    2. 用工厂方法,屏蔽创建过程
    2020-01-31
  • 高源
    仔细学习分析一下重构后带来的好处,解决了哪些问题
    2020-01-31
  • ちよくん
    打卡
    2020-01-31
收起评论
9
返回
顶部