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

35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”

注释
单元测试
可测试性
可读性
函数命名合理性
获取主机名失败的处理
细节处理
设计思想、原则、模式
知其然知其所以然
代码质量追求
重构
课堂讨论
开发思想
代码质量
总结

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

上一节课中,我们结合 ID 生成器代码讲解了如何发现代码质量问题。虽然 ID 生成器的需求非常简单,代码行数也不多,但看似非常简单的代码,实际上还是有很多优化的空间。综合评价一下的话,小王的代码也只能算是“能用”、勉强及格。我们大部分人写出来的代码都能达到这个程度。如果想要在团队中脱颖而出,我们就不能只满足于这个 60 分及格,大家都能做的事情,我们要做得更好才行。
上一节课我们讲了,为什么这份代码只能得 60 分,这一节课我们再讲一下,如何将 60 分的代码重构为 80 分、90 分,让它从“能用”变得“好用”。话不多说,让我们正式开始今天的学习吧!

回顾代码和制定重构计划

为了方便你查看和对比,我把上一节课中的代码拷贝到这里。
public class IdGenerator {
private static final Logger logger = LoggerFactory.getLogger(IdGenerator.class);
public static String generate() {
String id = "";
try {
String hostName = InetAddress.getLocalHost().getHostName();
String[] tokens = hostName.split("\\.");
if (tokens.length > 0) {
hostName = tokens[tokens.length - 1];
}
char[] randomChars = new char[8];
int count = 0;
Random random = new Random();
while (count < 8) {
int randomAscii = random.nextInt(122);
if (randomAscii >= 48 && randomAscii <= 57) {
randomChars[count] = (char)('0' + (randomAscii - 48));
count++;
} else if (randomAscii >= 65 && randomAscii <= 90) {
randomChars[count] = (char)('A' + (randomAscii - 65));
count++;
} else if (randomAscii >= 97 && randomAscii <= 122) {
randomChars[count] = (char)('a' + (randomAscii - 97));
count++;
}
}
id = String.format("%s-%d-%s", hostName,
System.currentTimeMillis(), new String(randomChars));
} catch (UnknownHostException e) {
logger.warn("Failed to get the host name.", e);
}
return id;
}
}
前面讲到系统设计和实现的时候,我们多次讲到要循序渐进、小步快跑。重构代码的过程也应该遵循这样的思路。每次改动一点点,改好之后,再进行下一轮的优化,保证每次对代码的改动不会过大,能在很短的时间内完成。所以,我们将上一节课中发现的代码质量问题,分成四次重构来完成,具体如下所示。
第一轮重构:提高代码的可读性
第二轮重构:提高代码的可测试性
第三轮重构:编写完善的单元测试
第四轮重构:所有重构完成之后添加注释
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了通过逐步重构的方式,将一个ID生成器的代码从“能用”重构为“好用”。作者提出了四次重构计划,包括提高代码的可读性、可测试性、编写完善的单元测试以及添加注释。文章详细讲解了第一轮重构的具体步骤,包括对接口和实现类的命名方式进行了深入分析。接着,介绍了第二轮重构,主要包括将generate()函数定义为普通函数,以及对两个函数的访问权限和注释进行了调整。在第三轮重构中,作者为代码补全了单元测试,重点测试了逻辑复杂的部分。最后,文章讨论了如何定义函数的功能,并根据不同的功能定义编写了相应的单元测试。通过实际案例深入讲解了代码重构的过程和技巧,对于想要提高代码质量的开发者具有很高的参考价值。文章还强调了对代码质量的追求、了解优秀代码设计的演变过程、细节处理对代码质量的重要性等开发思想。

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

全部留言(125)

  • 最新
  • 精选
  • 马哲富
    看到有人说这个专栏写得不好,我忍不住要留个言给这个专栏叫叫好,这个专栏写得很好,非常好,只恨自己水平有限,不能完全吸收,顶这个专栏!

    编辑回复: 哈哈,没事的,各有自己的判断,不可能让大家都觉得好,我们虚心相待,尽力而为。遇到问题,解决问题。

    2020-02-15
    10
    58
  • 辣么大
    对于在ID generator中方法里写到 void foo(){ Random random = new Random(); } 有个疑问: 1、为什么不声明成静态变量? 2、能用成员变量么?而不是写成局部变量

    作者回复: 也可以,不过尽量的缩小变量的作用域,代码可读性也好,毕竟random只会用在某个函数中,而不是用在多个函数中,放到局部函数中,也符合封装的特性,不暴露太多细节。

    2020-01-22
    3
    13
  • evolution
    代码的演变过程,真的是干货满满。不知道争哥有没有架构方面的演变课程?

    作者回复: 感谢认可,暂时没有呢

    2020-01-22
    2
    10
  • 提姆
    老師你好,想問一下有關測試的問題RandomIdGeneratorTest,為什麼不是分幾個Test Case去對generate做測試而是要拆出protected方法去做測試呢?

    作者回复: 拆出来的目的并非为了单元测试,更重要的是逻辑清晰,可读性好。之所以设置成potected的,是因为private的没法写单元测试。

    2020-07-13
    3
    6
  • 牛顿的烈焰激光剑
    老师,对于获取 hostname(getLastfieldOfHostName()),我的想法是用 static 代码块,只在类加载的时候执行一次。请问这样处理的话会不会有什么坏处?

    作者回复: 有可能hostname会改变,你的代码就获取不到最新的hostname

    2020-01-25
    2
    3
  • 一颗大白菜
    34行代码是不是写错了? Assert.assertTrue(('0' < c && c > '9') || ('a' < c && c > 'z') || ('A' < c && c < 'Z'));

    作者回复: 好像没有吧

    2020-01-22
    5
    3
  • Ken张云忠
    读小争哥的注释就是种欣赏,小争哥的英文表达是怎么一步步积累的? 我认为动词和介词是英文的精髓,还有英文的语法

    作者回复: 我英语也不好,多花点心思优化一下,实在不行,写中文注释也是可以的

    2020-01-22
    3
    3
  • 冬渐暖
    看了下您的代码,请教下 针对同一个service,有必要对各种情况都写一个@test吗?平时我都是一个接口一个test,如果有不同的条件,就直接在这个的入参上面改。 不然某个测试类的代码会很大,也没有必要对一个接口一个类,而是一个综合业务一个test类。

    作者回复: 一般来讲,一个单元测试类对应一个类。你说的可能更像是集成测试了。

    2020-07-06
    2
  • 云宝
    generateRandomAlphameric()方法的测试用例需要改为: Assert.assertTrue(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));

    作者回复: 嗯嗯,我改下

    2020-11-17
  • JUNLONG
    测试代码中的testGenerateRandomAlphameric()函数的for循环中的前两个范围判断打错了,应为:('0' < c && c < '9') || ('a' < c && c < 'z') 。 RandomIdGeneratorTest()函数中的一个#打成了$

    作者回复: 嗯嗯,多谢指出,我改下

    2020-06-16
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部