Skip to content

Commit e1363f7

Browse files
committed
docs(golang): 更新设计模式笔记。
更新状态模式笔记内容。 更新策略模式笔记内容。 更新模版方法模式笔记内容。 更新访问者模式笔记内容。
1 parent 0b9800b commit e1363f7

File tree

6 files changed

+626
-26
lines changed

6 files changed

+626
-26
lines changed
60 KB
Loading
108 KB
Loading

docs/zh/后端/01-Golang/03-Golang设计模式/21-状态模式.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -458,24 +458,24 @@ func TestVendingMachine(t *testing.T) {
458458

459459
### 实现说明
460460

461-
1. Context 持有 State:VendingMachine 结构体持有所有可能的状态实例(如 hasItemState 等),并在 currentState 字段中记录当前激活的状态。
462-
2. 委托机制:VendingMachine 的操作方法(如 RequestItem)并不直接执行业务逻辑,而是直接调用 currentState.RequestItem(),实现了行为的动态切换。
463-
3. 循环依赖处理:在 Go 语言中,具体状态类(如 HasItemState)通常需要持有 VendingMachine 的引用以便调用 SetState 触发状态流转。为了避免包的循环导入(import cycle),通常将 Context 和 State 接口定义在同一个包中。
464-
4. 状态流转:状态的切换逻辑被封装在具体状态类的方法中(例如 HasMoneyState.DispenseItem 执行完后,根据库存判断是切换回 HasItemState 还是 NoItemState)。这使得状态流转逻辑局部化,便于阅读。
461+
1. `Context` 持有 `State:VendingMachine` 结构体持有所有可能的状态实例(如 `hasItemState` 等),并在 `currentState` 字段中记录当前激活的状态。
462+
2. 委托机制:`VendingMachine` 的操作方法(如 `RequestItem`)并不直接执行业务逻辑,而是直接调用 `currentState.RequestItem()`,实现了行为的动态切换。
463+
3. 循环依赖处理:在 Go 语言中,具体状态类(如 `HasItemState`)通常需要持有 `VendingMachine` 的引用以便调用 `SetState` 触发状态流转。为了避免包的循环导入(`import cycle`),通常将 `Context``State` 接口定义在同一个包中。
464+
4. 状态流转:状态的切换逻辑被封装在具体状态类的方法中(例如 `HasMoneyState.DispenseItem` 执行完后,根据库存判断是切换回 `HasItemState` 还是 `NoItemState`)。这使得状态流转逻辑局部化,便于阅读。
465465

466466
## 优点与缺点
467467

468468
**优点:**
469469

470470
- 封装性强:将与特定状态相关的行为局部化到一个类中,并且将不同状态的行为分割开来。
471-
- 消除条件语句:避免了在 Context 类中出现大量的 if-else 或 switch-case 语句,代码更清晰。
472-
- 显式转换:状态转换在代码中是显式的(通过 SetState),而不是通过修改某些变量值(如 status = 1)隐式实现。
473-
- 符合开闭原则:新增状态只需增加新的具体状态类,无需修改 Context 源码。
471+
- 消除条件语句:避免了在 `Context` 类中出现大量的 `if-else``switch-case` 语句,代码更清晰。
472+
- 显式转换:状态转换在代码中是显式的(通过 `SetState`),而不是通过修改某些变量值(如 `status = 1`)隐式实现。
473+
- 符合开闭原则:新增状态只需增加新的具体状态类,无需修改 `Context` 源码。
474474

475475
**缺点:**
476476

477477
- 类爆炸:如果状态很多,会产生大量的具体状态类,增加系统复杂度。
478-
- 依赖耦合:具体状态类通常需要依赖 Context 类来执行状态切换,导致耦合度较高。
478+
- 依赖耦合:具体状态类通常需要依赖 `Context` 类来执行状态切换,导致耦合度较高。
479479
- 逻辑分散:状态流转逻辑分散在各个具体状态类中,不如集中在一个地方(如状态机表)容易总览全局。
480480

481481
## 适用场景
@@ -485,14 +485,14 @@ func TestVendingMachine(t *testing.T) {
485485
- 行为随状态改变:对象的行为依赖于其状态(属性),并且必须在运行时根据状态改变其行为。
486486
- 条件语句复杂:代码中包含大量与对象状态有关的条件语句,导致代码难以维护。
487487
- 工作流/订单系统:如订单状态(待支付、已支付、发货、收货、取消),审批流(草稿、待审批、驳回、通过)等场景。
488-
- TCP 连接:如 TCP 连接的不同状态(Established, Listening, Closed)对应不同的行为。
488+
- TCP 连接:如 TCP 连接的不同状态(`Established`, `Listening`, `Closed`)对应不同的行为。
489489

490490
## 注意事项
491491

492-
- Go 语言特性:Go 不支持类继承,状态模式通过接口(Interface)和组合(Composition)实现。
493-
- 并发安全:在并发环境下,如果多个 Goroutine 同时操作 Context,需要在 Context(如 VendingMachine)的方法中加锁(sync.Mutex),以防止状态切换时出现数据竞争。
494-
- 状态复用:如果具体状态类没有内部成员变量(无状态的对象),可以设计为单例模式,在多个 Context 实例间共享,以节省内存。
495-
- 与策略模式的区别:策略模式通常由客户端指定使用哪种策略,且策略之间通常相互独立;而状态模式的状态由 Context 或 状态自身管理,且状态之间存在流转关系。
492+
- Go 语言特性:Go 不支持类继承,状态模式通过接口(`Interface`)和组合(`Composition`)实现。
493+
- 并发安全:在并发环境下,如果多个 `Goroutine` 同时操作 `Context`,需要在 `Context`(如 `VendingMachine`)的方法中加锁(`sync.Mutex`),以防止状态切换时出现数据竞争。
494+
- 状态复用:如果具体状态类没有内部成员变量(无状态的对象),可以设计为单例模式,在多个 `Context` 实例间共享,以节省内存。
495+
- 与策略模式的区别:策略模式通常由客户端指定使用哪种策略,且策略之间通常相互独立;而状态模式的状态由 `Context` 或 状态自身管理,且状态之间存在流转关系。
496496

497497
## 参考资料
498498

docs/zh/后端/01-Golang/03-Golang设计模式/22-策略模式.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ editLink: false
3131

3232
```mermaid
3333
---
34-
title: 策略模式 (含 Client)
34+
title: 策略模式
3535
---
3636
classDiagram
3737
direction LR
@@ -270,16 +270,16 @@ func TestStrategyPattern(t *testing.T) {
270270

271271
优点:
272272

273-
- 符合开闭原则:可以在不修改 Context 代码的情况下引入新的策略。
274-
- 避免多重条件判断:消除了代码中大量的 if-else 或 switch-case 语句,这些语句通常用于选择不同的算法。
275-
- 算法复用:不同的 Context 可以共享同一个策略实例(如果策略是无状态的)。
273+
- 符合开闭原则:可以在不修改 `Context` 代码的情况下引入新的策略。
274+
- 避免多重条件判断:消除了代码中大量的 `if-else``switch-case` 语句,这些语句通常用于选择不同的算法。
275+
- 算法复用:不同的 `Context` 可以共享同一个策略实例(如果策略是无状态的)。
276276
- 关注点分离:将算法的实现细节从使用它的业务逻辑中剥离出来。
277277

278278
缺点:
279279

280280
- 客户端需知晓策略:客户端必须知道所有的策略类,并理解它们之间的区别,以便选择合适的策略。
281281
- 类数量增加:每个新的策略都需要创建一个新的类,会导致类的数量增加。
282-
- 通信开销:如果策略接口定义了复杂的数据传递机制(例如通过 Context 传递参数),可能会造成一定的调用开销,或者导致某些简单策略被迫接收无用的参数。
282+
- 通信开销:如果策略接口定义了复杂的数据传递机制(例如通过 `Context` 传递参数),可能会造成一定的调用开销,或者导致某些简单策略被迫接收无用的参数。
283283

284284
## 适用场景
285285

@@ -292,11 +292,11 @@ func TestStrategyPattern(t *testing.T) {
292292

293293
## 注意事项
294294

295-
- Go 语言特性:Go 中的函数也是一等公民(First-class citizen)。在简单的策略模式中,如果策略只包含一个方法(如只有 Pay),甚至不需要定义接口和结构体,直接将函数作为策略传递给 Context 也是一种常见的 Go 风格写法(Functional Options Pattern 或直接传 func)。
295+
- Go 语言特性:Go 中的函数也是一等公民(First-class citizen)。在简单的策略模式中,如果策略只包含一个方法(如只有 Pay),甚至不需要定义接口和结构体,直接将函数作为策略传递给 `Context` 也是一种常见的 Go 风格写法(Functional Options Pattern 或直接传 func)。
296296
- 无状态策略:如果具体策略类没有成员变量(即无状态),建议作为单例使用,避免重复创建对象,节省内存。
297297
- 与状态模式的区别:
298298
- 策略模式:客户端通常主动指定要使用的策略。策略之间通常是独立的,不知道彼此的存在。
299-
- 状态模式:状态之间的切换通常由状态对象自己或 Context 自动触发。客户端通常不直接指定下一个状态。
299+
- 状态模式:状态之间的切换通常由状态对象自己或 `Context` 自动触发。客户端通常不直接指定下一个状态。
300300

301301
## 参考资料
302302

0 commit comments

Comments
 (0)