Skip to content

Commit

Permalink
update: refactory state
Browse files Browse the repository at this point in the history
  • Loading branch information
hscspring committed Oct 22, 2022
1 parent 2fa4a16 commit 5c00af5
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 28 deletions.
2 changes: 1 addition & 1 deletion docs/content/design_pattern/abstract_factory.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
- 创建对应的 `ConcreteProduct`,并重写自己的插入方法。


另外一些注意事项包括
下面是一些注意事项

- 可以借助反射使用变量来初始化不同产品,以减少显式、固定地分支判断。
- 可以根据需要与简单工厂、工厂方法结合起来使用。
Expand Down
217 changes: 190 additions & 27 deletions docs/content/design_pattern/state.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,226 @@
# 状态模式

## 设计动机
## 模式引入

**问题引入**
### 问题描述

假设我们要描述一名员工一天的工作状态,正常来看是比较简单的,直接从某个状态开始,根据一些条件判断切换到其他状态。但假设我们切换到一个状态后又可能需要根据另外的条件再次切换,比如本来在看论文,突然领导喊开会,那就要切换到开会状态,开完会线上出了个问题,此时又要切换到写代码状态,当然也可能回来继续看论文。这需要各种条件判断(每个状态都要判断是否满足跳到其他状态),不光是写起来复杂,新增状态和更新状态更是会变得异常麻烦。
假设我们要描述一名员工一天不同时间的工作状态,正常来看是比较简单的,直接从早上上班开始,根据一些时间变化切换到其他状态。但假设我们切换到一个状态后又可能需要根据另外的条件再次切换,比如本来在写代码,到晚上了还没写完,继续写。好不容易写完了,需要切换到下班状态;或者没写完,直到睡着还没写完。这需要各种条件判断(每个状态都要判断是否满足跳到其他状态),不光是写起来复杂,新增状态和更新状态更是会变得异常麻烦。

此时,很自然我们就会想要将一个个状态独立出来,为每个状态设置所有需要的切换条件,在状态发生变化时改变对应的行为。这样我们只需要关心状态从 A 转换到 B,而不用管各种判断逻辑。具体来说,我们可以将看论文、开会、写代码、下班等都设置为一个个状态,每个状态内部有自己的转换判断,比如看论文可以切换到开会、写代码和下班,但不需要切换到看论文,下班也不切换到开会等等。这样每种状态有自己的判断逻辑,行为和状态绑定,外部只需直接切换到想要的状态即可。
此时,很自然我们就会想要将一个个状态独立出来,为每个状态设置所有需要的切换条件,在状态发生变化时改变对应的行为。这样我们只需要关心状态从 A 转换到 B,而不用管各种判断逻辑。

**模式引入**
### 模式定义

当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移到表示不同状态的一系列类中,可以简化复杂的判断逻辑。如果状态的判断很简单,就不需要使用状态模式了。状态模式将不同状态的行为分割开,将与特定状态相关的行为局部化(放入一个对象),通过新的类很容易新增状态。状态模式通过把各种状态的转移逻辑分不到 State 的子类之间,减少相互依赖,消除庞大的条件分支判断。
当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移到表示不同状态的一系列类中,可以简化复杂的判断逻辑。

## 模式介绍
状态模式允许一个对象在内部状态改变时改变它的行为,这个对象看起来好像改变了类。

**模式定义**
状态模式将不同状态的行为分割开,将与特定状态相关的行为局部化(放入一个对象),通过新的类很容易新增状态。状态模式通过把各种状态的转移逻辑分不到 State 的子类之间,减少相互依赖,消除庞大的条件分支判断。

状态模式允许一个对象在内部状态改变时改变它的行为,这个对象看起来好像改变了类。
### 问题分析

考虑用状态模式来实现上面的问题,具体来说,我们可以将不同时间段、休息、睡觉等都设置为一个个状态,每个状态内部有自己的转换判断,比如上午可以切换到下午,但不能直接切换到下班。这样每种状态有自己的判断逻辑,行为和状态绑定,外部只需直接切换到想要的状态即可。

## 模式实现

### 解决方案

**实现方式**
主要是定义一系列状态以及在状态内部实现该状态的转移逻辑。

- 抽象状态 `State`,定义一个抽象方法,如 `writeProgram`
- 实现不同的具体状态 `Concrete` 类:`ForenoonState``NoonState``AfternoonState`等,并重写抽象方法。
- 实现一个上下文类:`Work` 用来管理状态的变化。

下面是一些注意事项:

- 将状态的转换放到具体的状态类内部。
- 通过上下文直接或通过参数改变状态,而不同的状态对应不同的行为。

**模式结构**
### 代码实现

`State` 类:

```java
public abstract class State {
public abstract void writeProgram(Work w);
}
```

`ConcreteState` 类:

```java
public class ForenoonState extends State {
@Override
public void writeProgram(Work w) {
if(w.getHour() < 12) {
System.out.println("当前时间:" + w.getHour() + "点 上午工作,精神百倍");
}else {
w.setState(new NoonState());
w.writeProgram();
}
}
}

public class NoonState extends State {
@Override
public void writeProgram(Work w) {
if(w.getHour() < 13) {
System.out.println("当前时间:" + w.getHour() + "点 饿了,午饭;犯困,午休");
}else {
w.setState(new AfternoonState());
w.writeProgram();
}
}
}

public class AfternoonState extends State {
@Override
public void writeProgram(Work w) {
if(w.getHour() < 17){
System.out.println("当前时间 " + w.getHour() + "点 下午状态还不错,继续努力");
}else {
w.setState(new EveningState());
w.writeProgram();
}
}
}

public class RestState extends State {
@Override
public void writeProgram(Work w) {
System.out.println("当前时间:" + w.getHour() + "点 下班回家了");
}
}

public class SleepingState extends State {
@Override
public void writeProgram(Work w) {
System.out.println("当前时间:" + w.getHour() + "点 不行了,睡着了。");
}
}

public class EveningState extends State {
@Override
public void writeProgram(Work w) {
if(w.isFinish()) {
w.setState(new RestState());
w.writeProgram();
}else {
if(w.getHour() < 21) {
System.out.println("当前时间:" + w.getHour() + "点 加班哦,疲累之极");
}else {
w.setState(new SleepingState());
w.writeProgram();
}
}
}
}


```

`Context` 类:

```java
public class Work {
private State current;

public Work() {
current = new ForenoonState();
}

private int hour;
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}

private boolean finish = false;
public boolean isFinish() {
return finish;
}
public void setFinish(boolean finish) {
this.finish = finish;
}

public void setState(State s) {
current = s;
}

public void writeProgram() {
current.writeProgram(this);
}
}
```

`Main` 方法:

```java
public class Main {
public static void main(String[] args) {
Work emergencyProjects = new Work();
emergencyProjects.setHour(9);
emergencyProjects.writeProgram();

emergencyProjects.setHour(10);
emergencyProjects.writeProgram();

emergencyProjects.setHour(12);
emergencyProjects.writeProgram();

emergencyProjects.setHour(13);
emergencyProjects.writeProgram();

emergencyProjects.setHour(14);
emergencyProjects.writeProgram();

emergencyProjects.setHour(17);
emergencyProjects.writeProgram();

emergencyProjects.setFinish(false);
emergencyProjects.writeProgram();

emergencyProjects.setHour(19);
emergencyProjects.writeProgram();

emergencyProjects.setHour(22);
emergencyProjects.writeProgram();
}
}
```

### 结构组成

![](img/state/state.jpeg)

**代码实现**

- C++ 实现:[链接](https://github.com/datawhalechina/sweetalk-design-pattern/tree/main/src/design_patterns/cpp/state)
- Java 实现:[链接](https://github.com/datawhalechina/sweetalk-design-pattern/tree/main/src/design_patterns/java/state)
- Python 实现:[链接](https://github.com/datawhalechina/sweetalk-design-pattern/tree/main/src/design_patterns/python/state)
## 模式评价

## 使用场景
### 适用场景

**适合场景**
- 当一个对象的行为取决于它的状态,并且必须在运行时根据状态改变其行为时。比如上面例子中,可以根据设置任务完成状态改变具体行为。
- 某业务有多个状态,且状态变化依赖大量分支判断时。上面的例子使用状态模式后将状态的逻辑判断转移到具体状态内,避免了多条分支判断。

- 当一个对象的行为取决于它的状态,并且必须在运行时根据状态改变其行为时。
- 某业务有多个状态,且状态变化依赖大量分支判断时。
如果状态的判断很简单,就不需要使用状态模式了。

**实际应用**
### 实际应用

实际中的例子:多个状态与行为变化。

- 有限状态机。
- TCP 连接协议。
- TCP 连接协议。主要是连接状态的管理。
- 其他涉及一系列状态变化的应用。

## 模式评价
### 优点缺点

**模式优点**
状态模式的优点包括:

- 状态即行为,通过状态的改变引起行为的变化,消除了条件逻辑,代码更简单清晰。
- 与特定状态相关的代码都放在一起,提高了聚合性。
- 添加一个状态非常容易,提高了扩展性。

**不足之处**
状态模式的缺点包括:

- 可能会创建太多功能较为单一的类。
- 新的行为可能涉及大量改动。
- 可能会创建太多功能较为单一的类。当状态粒度太小时,这种情况尤其明显。
- 新的行为可能涉及大量改动。因为该行为可能涉及到多个类,此时需要更新所有涉及到的类,将新的行为逻辑加入。
- 状态少或很少改变时有点小题大做,此时不应使用状态模式。

0 comments on commit 5c00af5

Please sign in to comment.