C++教程的实践小结

导言

最近实践了[Milo Yip] (https://www.zhihu.com/people/miloyip/answers) 的从零开始的 JSON 库教程,文中的几点自己觉得最有收获的地方。当然在课程设计上,用%进度来显示完成的情况,真的是非常用心。

JSON 是什么

JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为ECMA-404

虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。

例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"title": "Design Patterns",
"subtitle": "Elements of Reusable Object-Oriented Software",
"author": [
"Erich Gamma",
"Richard Helm",
"Ralph Johnson",
"John Vlissides"
],
"year": 2009,
"weight": 1.8,
"hardcover": true,
"publisher": {
"Company": "Pearson Education",
"Country": "India"
},
"website": null
}

网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。

从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:

  • null: 表示为 null
  • boolean: 表示为 true 或 false
  • number: 一般的浮点数表示方式,在下一单元详细说明
  • string: 表示为 “…”
  • array: 表示为 [ … ]
  • object: 表示为 { … }

我们要实现的 JSON 库,主要是完成 3 个需求:

  1. 把 JSON 文本解析为一个树状数据结构(parse)。
  2. 提供接口访问该数据结构(access)。
  3. 把数据结构转换成 JSON 文本(stringify)。

requirement


虽然JSON在项目中有使用,却很少去关注,JSON遵循了什么协议要求,在以上的内容还看出了,关于需求的分解和实践上的有突破点,这反而是刚入行的很少去注意这样了。


单元测试

在做练习题时,都是以 printfcout 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。

常用的单元测试框架有 xUnit 系列,如 C++ 的 Google Test、C# 的 NUnit。我们为了简单起见,会编写一个极简单的单元测试方式。

一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:

  1. 加入一个测试。
  2. 运行所有测试,新的测试应该会失败。
  3. 编写实现代码。
  4. 运行所有测试,若有测试失败回到3。
  5. 重构代码。
  6. 回到 1。

TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。

但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。


这样的规范的开发流程,在没有经历过很规范的项目或者团队,真的没有机会有这样的机会去了解。在自己实际开发流程中,是自己设定边界值去测试,而不是加入单元测试的来测试。规范的好处在于降低理解业务的内容难度,即使其他人的业务也可以保证正确


重构

在软件工程中,代码重构(code refactoring)是指在不改变软件外在行为时,修改代码以改进结构。代码重构十分依赖于单元测试,因为我们是通过单元测试去维护代码的正确性。有了足够的单元测试,我们可以放胆去重构,尝试并评估不同的改进方式,找到合乎心意而且能通过单元测试的改动,我们才提交它。

最后,我希望指出,软件的架构难以用单一标准评分,重构时要考虑平衡各种软件品质。例如上述把 3 个函数合并后,优点是减少重复的代码,维护较容易,但缺点可能是带来性能的少量影响。


重构:减少重复的代码,维护较容易。在实际项目中,进行方法提取,减少重复的代码,以达到代码简洁和清晰。当然也可以实现功能中发现,发现有方法可以复用,进行方法提取。


JSON数字语法

回归正题,本单元的重点在于解析 JSON number 类型。我们先看看它的语法:

1
2
3
4
number = [ "-" ] int [ frac ] [ exp ]
int = "0" / digit1-9 *digit
frac = "." 1*digit
exp = ("e" / "E") ["-" / "+"] 1*digit

number 是以十进制表示,它主要由 4 部分顺序组成:负号、整数、小数、指数。只有整数是必需部分。注意和直觉可能不同的是,正号是不合法的。

整数部分如果是 0 开始,只能是单个 0;而由 1-9 开始的话,可以加任意数量的数字(0-9)。也就是说,0123 不是一个合法的 JSON 数字。

小数部分比较直观,就是小数点后是一或多个数字(0-9)。

JSON 可使用科学记数法,指数部分由大写 E 或小写 e 开始,然后可有正负号,之后是一或多个数字(0-9)。

JSON 标准 ECMA-404 采用图的形式表示语法,也可以更直观地看到解析时可能经过的路径:


上面这张图,我看到那一瞬间是很懵,这张图到底讲了什么。后来看来代码部分了。对于Milo大神的思考路径刚到敬佩,思考顺序流程很清晰,而且用一张图的就能表达清楚,说明他的理解很深刻。作为菜鸟也就是这样的学习过程中,才会有成长。


文章目录
  1. 1. 导言
  2. 2. JSON 是什么
    1. 2.0.1. 虽然JSON在项目中有使用,却很少去关注,JSON遵循了什么协议要求,在以上的内容还看出了,关于需求的分解和实践上的有突破点,这反而是刚入行的很少去注意这样了。
  • 3. 单元测试
    1. 3.0.1. 这样的规范的开发流程,在没有经历过很规范的项目或者团队,真的没有机会有这样的机会去了解。在自己实际开发流程中,是自己设定边界值去测试,而不是加入单元测试的来测试。规范的好处在于降低理解业务的内容难度,即使其他人的业务也可以保证正确
  • 4. 重构
    1. 4.1. 重构:减少重复的代码,维护较容易。在实际项目中,进行方法提取,减少重复的代码,以达到代码简洁和清晰。当然也可以实现功能中发现,发现有方法可以复用,进行方法提取。
  • 5. JSON数字语法
    1. 5.1. 上面这张图,我看到那一瞬间是很懵,这张图到底讲了什么。后来看来代码部分了。对于Milo大神的思考路径刚到敬佩,思考顺序流程很清晰,而且用一张图的就能表达清楚,说明他的理解很深刻。作为菜鸟也就是这样的学习过程中,才会有成长。
  • |