Skip to content

Commit 74d6cef

Browse files
committed
docs: 修正系统设计文档中的事实与示例
1 parent 4ce6bcc commit 74d6cef

27 files changed

Lines changed: 585 additions & 597 deletions

docs/system-design/J2EE基础知识.md

Lines changed: 31 additions & 47 deletions
Large diffs are not rendered by default.

docs/system-design/basis/RESTfulAPI.md

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,19 @@ POST /classes:新建一个班级
4545

4646
**RESTful API 可以让你看到 URL+Http Method 就知道这个 URL 是干什么的,让你看到了 HTTP 状态码(status code)就知道请求结果如何。**
4747

48-
像咱们在开发过程中设计 API 的时候也应该至少要满足 RESTful API 的最基本的要求(比如接口中尽量使用名词,使用 `POST` 请求创建资源,`DELETE` 请求删除资源等等,示例:`GET /notes/id`:获取某个指定 id 的笔记的信息)。
48+
像咱们在开发过程中设计 API 的时候也应该至少要满足 RESTful API 的最基本的要求(比如接口中尽量使用名词,使用 `POST` 请求创建资源,`DELETE` 请求删除资源等等,示例:`GET /notes/{id}`:获取某个指定 id 的笔记的信息)。
4949

5050
## 解读 REST
5151

5252
**REST**`REpresentational State Transfer` 的缩写。这个词组的翻译过来就是“**表现层状态转化**”。
5353

54-
这样理解起来甚是晦涩,实际上 REST 的全称是 **Resource Representational State Transfer** ,直白地翻译过来就是 **“资源”在网络传输中以某种“表现形式”进行“状态转移”** 。如果还是不能继续理解,请继续往下看,相信下面的讲解一定能让你理解到底啥是 REST 。
54+
这样理解起来甚是晦涩,直白地说,REST 描述的是客户端通过资源的“表现形式”,从一个应用状态转移到另一个应用状态。如果还是不能继续理解,请继续往下看,相信下面的讲解一定能让你理解到底啥是 REST 。
5555

5656
我们分别对上面涉及到的概念进行解读,以便加深理解,实际上你不需要搞懂下面这些概念,也能看懂我下一部分要介绍到的内容。不过,为了更好地能跟别人扯扯 “RESTful API”我建议你还是要好好理解一下!
5757

58-
- **资源(Resource)**:我们可以把真实的对象数据称为资源。一个资源既可以是一个集合,也可以是单个个体。比如我们的班级 classes 是代表一个集合形式的资源,而特定的 class 代表单个个体资源。每一种资源都有特定的 URI(统一资源标识符)与之对应,如果我们需要获取这个资源,访问这个 URI 就可以了,比如获取特定的班级:`/class/12`。另外,资源也可以包含子资源,比如 `/classes/classId/teachers`:列出某个指定班级的所有老师的信息
58+
- **资源(Resource)**:我们可以把真实的对象数据称为资源。一个资源既可以是一个集合,也可以是单个个体。比如我们的班级 classes 是代表一个集合形式的资源,而特定的 class 代表单个个体资源。每一种资源都有特定的 URI(统一资源标识符)与之对应,如果我们需要获取这个资源,访问这个 URI 就可以了,比如获取特定的班级:`/classes/12`。另外,资源也可以包含子资源,比如 `/classes/{classId}/teachers`:列出某个指定班级的所有老师的信息
5959
- **表现形式(Representational)**:"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式比如 `json``xml``image`,`txt` 等等叫做它的"表现层/表现形式"。
60-
- **状态转移(State Transfer)**:大家第一眼看到这个词语一定会很懵逼?内心 BB:这尼玛是啥啊? 大白话来说 REST 中的状态转移更多地描述的服务器端资源的状态,比如你通过增删改查(通过 HTTP 动词实现)引起资源状态的改变。ps:互联网通信协议 HTTP 协议,是一个无状态协议,所有的资源状态都保存在服务器端
60+
- **状态转移(State Transfer)**:大家第一眼看到这个词语一定会很懵逼?内心 BB:这尼玛是啥啊? 大白话来说,客户端通过资源的表现形式以及其中的链接等控制信息,从一个应用状态转移到另一个应用状态。通过 HTTP 方法进行增删改查,也可能引起服务器端资源状态的改变。ps:HTTP 是一个无状态协议,服务器不需要在两次请求之间保存客户端的会话状态
6161

6262
综合上面的解释,我们总结一下什么是 RESTful 架构:
6363

@@ -72,16 +72,18 @@ POST /classes:新建一个班级
7272
### 动作
7373

7474
- `GET`:请求从服务器获取特定资源。举个例子:`GET /classes`(获取所有班级)
75-
- `POST`:在服务器上创建一个新的资源。举个例子:`POST /classes`(创建班级)
76-
- `PUT`:更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:`PUT /classes/12`(更新编号为 12 的班级)
77-
- `DELETE`:从服务器删除特定的资源。举个例子:`DELETE /classes/12`(删除编号为 12 的班级)
78-
- `PATCH`:更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少,这里就不举例子了。
75+
- `POST`:让目标资源按照自身语义处理请求内容,常用于创建资源。举个例子:`POST /classes`(创建班级)
76+
- `PUT`:创建或替换目标资源的当前状态(客户端通常提供更新后的完整资源)。举个例子:`PUT /classes/12`(更新编号为 12 的班级)
77+
- `DELETE`:移除目标 URI 与当前资源功能之间的关联。举个例子:`DELETE /classes/12`(删除编号为 12 的班级)
78+
- `PATCH`:更新服务器上的资源(客户端提供更改的属性,可以看作是部分更新),使用的比较少,这里就不举例子了。
79+
80+
其中,`GET` 是安全且幂等的,`PUT``DELETE` 是幂等的,`PATCH` 默认不保证幂等。
7981

8082
### 路径(接口命名)
8183

8284
路径又称"终点"(endpoint),表示 API 的具体网址。实际开发中常见的规范如下:
8385

84-
1. **网址中不能有动词,只能有名词,API 中的名词也应该使用复数** 因为 REST 中的资源往往和数据库中的表对应,而数据库中的表都是同种记录的"集合"(collection)。如果 API 调用并不涉及资源(如计算翻译等操作)的话,可以用动词。比如:`GET /calculate?param1=11&param2=33`
86+
1. **资源型 HTTP API 的网址通常使用名词,名词常用复数形式** 这是一种常见的 URI 命名约定,并非 REST 强制约束。如果 API 调用不便抽象为资源(如计算翻译等操作)的话,也可以用动词。比如:`GET /calculate?param1=11&param2=33`
8587
2. **不用大写字母,建议用中杠 - 不用下杠 \_** 。比如邀请码写成 `invitation-code`而不是 ~~invitation_code~~
8688
3. **善用版本化 API**。当我们的 API 发生了重大改变而不兼容前期版本的时候,我们可以通过 URL 来实现版本化,比如 `http://api.example.com/v1``http://apiv1.example.com` 。版本不必非要是数字,只是数字用的最多,日期、季节都可以作为版本标识符,项目团队达成共识就可。
8789
4. **接口尽量使用名词,避免使用动词。** RESTful API 操作(HTTP Method)的是资源(名词)而不是动作(动词)。
@@ -108,7 +110,7 @@ DELETE /classes/{classId}/teachers/{ID}:删除某个指定班级下的指定
108110
/deleteAllActiveclasses
109111
```
110112

111-
理清资源的层次结构,比如业务针对的范围是学校,那么学校会是一级资源:`/schools`,老师: `/schools/teachers`,学生: `/schools/students` 就是二级资源。
113+
理清资源的层次结构,比如业务针对的范围是学校,那么学校会是一级资源:`/schools`,老师: `/schools/{schoolId}/teachers`,学生: `/schools/{schoolId}/students` 就是二级资源。
112114

113115
### 过滤信息(Filtering)
114116

@@ -136,24 +138,24 @@ GET /classes?page=1&size=10 //指定第1页,每页10个数据
136138
| | | 404 未找到 | |
137139
| | | 405 请求方法不对 | |
138140

139-
## RESTful 的极致 HATEOAS
141+
## REST 中的 HATEOAS
140142

141-
> **RESTful 的极致是 hateoas ,但是这个基本不会在实际项目中用到**
143+
> **在 Fielding 对 REST 的原始定义中,HATEOAS 是统一接口约束的一部分。不过,工程中很多被称为 REST API 的 HTTP/JSON API 并没有实现它**
142144
143-
上面是 RESTful API 最基本的东西,也是我们平时开发过程中最容易实践到的。实际上,RESTful API 最好做到 Hypermedia,即返回结果中提供链接,连向其他 API 方法,使得用户不查文档,也知道下一步应该做什么。
145+
上面是 RESTful API 最基本的东西,也是我们平时开发过程中最容易实践到的。HATEOAS 要求通过 Hypermedia 驱动应用状态,即返回结果中提供链接等控制信息,使得用户不查文档,也知道下一步应该做什么。
144146

145147
比如,当用户向 `api.example.com` 的根目录发出请求,会得到这样一个返回结果
146148

147149
```javascript
148150
{"link": {
149-
"rel": "collection https://www.example.com/classes",
151+
"rel": "collection",
150152
"href": "https://api.example.com/classes",
151153
"title": "List of classes",
152154
"type": "application/vnd.yourformat+json"
153155
}}
154156
```
155157

156-
上面代码表示,文档中有一个 `link` 属性,用户读取这个属性就知道下一步该调用什么 API 了。`rel` 表示这个 API 与当前网址的关系(collection 关系,并给出该 collection 的网址),`href` 表示 API 的路径,title 表示 API 的标题`type` 表示返回类型 `Hypermedia API` 的设计被称为[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS)
158+
上面代码表示,文档中有一个 `link` 属性,用户读取这个属性就知道下一步该调用什么 API 了。`rel` 表示目标资源与当前上下文的关系,`href` 表示目标资源的路径,`title` 表示链接的标题`type` 是目标资源表现形式的媒体类型提示。这样的 `Hypermedia API` 设计被称为[HATEOAS](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)
157159

158160
在 Spring 中有一个叫做 HATEOAS 的 API 库,通过它我们可以更轻松的创建出符合 HATEOAS 设计的 API。相关文章:
159161

@@ -165,6 +167,14 @@ GET /classes?page=1&size=10 //指定第1页,每页10个数据
165167

166168
## 参考
167169

170+
- [Fielding 论文:Representational State Transfer](https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)
171+
172+
- [RFC 9110:HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
173+
174+
- [RFC 5789:PATCH Method for HTTP](https://www.rfc-editor.org/rfc/rfc5789.html)
175+
176+
- [RFC 8288:Web Linking](https://www.rfc-editor.org/rfc/rfc8288.html)
177+
168178
- <https://RESTfulapi.net/>
169179

170180
- <https://www.ruanyifeng.com/blog/2014/05/restful_api.html>

docs/system-design/basis/naming.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,19 @@ serviceDiscovery、Serviceinstance、LRUCacheFactory
9999
getUserInfo()
100100
createCustomThreadPool()
101101
setNameFormat(String nameFormat)
102-
Uservice userService;
102+
UserService userService;
103103
```
104104

105105
反例:
106106

107107
```java
108108
GetUserInfo()、CreateCustomThreadPool()、setNameFormat(String NameFormat)
109-
Uservice user_service
109+
UserService user_service;
110110
```
111111

112112
### 蛇形命名法(snake_case)
113113

114-
**测试方法名、常量、枚举名称需要使用蛇形命名法(snake_case)**
114+
**测试方法名可以按团队约定使用蛇形命名法(snake_case),常量和枚举常量通常使用大写蛇形命名法。**
115115

116116
在蛇形命名法中,各个单词之间通过下划线“\_”连接,比如`should_get_200_status_code_when_request_is_valid``CLIENT_CONNECT_SERVER_FAILURE`
117117

@@ -128,7 +128,7 @@ void should_get_200_status_code_when_request_is_valid() {
128128
}
129129
```
130130

131-
反例
131+
另一种常见写法
132132

133133
```java
134134
@Test
@@ -151,7 +151,7 @@ void shouldGet200StatusCodeWhenRequestIsValid() {
151151

152152
**1、类名需要使用大驼峰命名法(UpperCamelCase)风格。方法名、参数名、成员变量、局部变量需要使用小驼峰命名法(lowerCamelCase)。**
153153

154-
**2、测试方法名、常量、枚举名称需要使用蛇形命名法(snake_case)**,比如`should_get_200_status_code_when_request_is_valid``CLIENT_CONNECT_SERVER_FAILURE`。并且,**测试方法名称要求全部小写,常量以及枚举名称需要全部大写。**
154+
**2、测试方法没有唯一正确的命名方式,可以根据团队约定使用蛇形命名法(snake_case)**,比如`should_get_200_status_code_when_request_is_valid`。常量和枚举常量通常使用大写蛇形命名法,比如`CLIENT_CONNECT_SERVER_FAILURE`;枚举类型仍然使用大驼峰命名法。
155155

156156
**3、项目文件夹名称使用串式命名法(kebab-case),比如`dubbo-registry`**
157157

@@ -196,7 +196,7 @@ public class AnnotationUtilsTest {
196196
}
197197
```
198198

199-
POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误
199+
POJO 类中布尔类型字段是否使用 `is` 前缀,需要结合访问器生成规则和序列化框架判断。通常可以将基本类型字段命名为 `active` 并提供 `isActive()`,将包装类型 `Boolean` 字段命名为 `active` 并提供 `getActive()`;如果框架推断结果不符合预期,可以通过显式访问器或序列化注解固定属性名
200200

201201
如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。
202202

docs/system-design/basis/refactoring.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ head:
1919
> - 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
2020
> - 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
2121
22-
用更贴近工程师的语言来说:**重构就是利用设计模式(如组合模式、策略模式、责任链模式)、软件设计原则(如 SOLID 原则、YAGNI 原则、KISS 原则)和重构手段(如封装、继承、构建测试体系)来让代码更容易理解,更易于修改。**
22+
用更贴近工程师的语言来说:**重构就是在不改变软件可观察行为的前提下,通过一系列小步的结构调整,让代码更容易理解,更易于修改。** 设计模式和软件设计原则可以为重构提供方向,自动化测试则是重构的重要保障。
2323

2424
软件设计原则指导着我们组织和规范代码,同时,重构也是为了能够尽量设计出尽量满足软件设计原则的软件。
2525

@@ -39,7 +39,7 @@ head:
3939

4040
## 为什么要重构?
4141

42-
在上面介绍重构定义的时候,我从比较抽象的角度介绍了重构的好处:重构的主要目的主要是提升代码&架构的灵活性/可扩展性以及复用性
42+
在上面介绍重构定义的时候,我从比较抽象的角度介绍了重构的好处:重构的主要目的是让代码更容易理解,降低后续修改的成本
4343

4444
如果对应到一个真实的项目,重构具体能为我们带来什么好处呢?
4545

@@ -114,11 +114,11 @@ Code Review 可以非常有效提高代码的整体质量,它会帮助我们
114114

115115
## 重构有哪些注意事项?
116116

117-
### 单元测试是重构的保护网
117+
### 自动化测试是重构的保护网
118118

119-
**单元测试可以为重构提供信心,降低重构的成本。我们要像重视生产代码那样,重视单元测试**
119+
**自动化测试可以为重构提供信心,降低重构的成本。单元测试通常是反馈最快的一层,集成测试、验收测试等也可以共同组成保护网。我们要像重视生产代码那样,重视测试代码**
120120

121-
另外,多提一句:持续集成也要依赖单元测试,当持续集成服务自动构建新代码之后,会自动运行单元测试来发现代码错误
121+
另外,多提一句:持续集成也要依赖快速、可靠的自动化测试,当持续集成服务自动构建新代码之后,会自动运行测试来发现代码错误
122122

123123
**怎样才能算单元测试呢?** 网上的定义很多,很抽象,很容易把人给看迷糊了。我觉得对于单元测试的定义主要取决于你的项目,一个函数甚至是一个类都可以看作是一个单元。就比如说我们写了一个计算个人股票收益率的方法,我们为了验证它的正确性专门为它写了一个单元测试。再比如说我们代码有一个类专门负责数据脱敏,我们为了验证脱敏是否符合预期专门为这个类写了一个单元测试。
124124

0 commit comments

Comments
 (0)