一个对象的行为其属性的动态变化,这样的属性叫状态,这类对象也叫做有状态的对象。当此类对象被某一事件修改其内部状态时,程序的行为也要随之改变

状态模式又称状态对象模式(Pattern of Objects for States)是用于解决对象的复杂状态及不同状态下的行为的一种模式。

模式定义

状态模式允许一个对象在其内部属性改变的时候改变其行为,这个对象看上去就像是改变了它的类一样

模式结构

状态模式涉及角色:

  • 环境角色(Context):定义客户感兴趣的接口,并保留一个具体状态类的实例
  • 抽象状态角色(State):定义一个借口,封装特定状态下的对应行为
  • 具体状态角色(ConcreteState):抽象状态角色的子类,每个子类实现了相关的行为

Tip:

  • 该图为UML图
  • 类包含3个组成部分,第一栏为类名,第二栏为属性,第三栏为方法
  • 属性和方法前可加一个可见性修饰符, + 号表示 public 修饰符, - 号表示 private 修饰符, # 号表示 protected 修饰符,省略表示包级可见。
  • 接口包含2个组成部分,第一栏为接口名,第二栏为方法,在接口名之上加上 <<interface>>

使用场景

比如游戏中一个用户的用过外挂违规次数属性,如果用户用过1~3次,每次警告制裁;3次以上,每次封号3天;5次以上,每次封号1周;到达10次,永久封号。

根据以上描述可以分为四种状态:

  • 警告
  • 封号3天
  • 封号1周
  • 永久封号

源码

环境角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class PunishManager {

//保存违规用户及次数
private Map<String, Integer> mPunishMap = new HashMap<>();


/**
* 获取违规用户及次数
*/
Map<String, Integer> getPunishMap() {
return mPunishMap;
}

/**
* 获取具体状态角色,封装转换规则
*
* @param oldPunishCount 违规次数
* @return 具体状态角色
*/
private PunishState getPunishState(Integer oldPunishCount) {

//推荐尽量少用else,如果超过3层if-else代码推荐使用卫语句
if (oldPunishCount <= 3) {
return new LowPunishState();
}

if (oldPunishCount <= 5) {
return new MidPunishState();
}

if (oldPunishCount < 10) {
return new HeightPunishState();
}

return new BlackPunishState();
}

/**
* 违规处理
*
* @param uid 用户ID
*/
public void punish(String uid) {
//获取之前违规次数
Integer oldPunishCount = mPunishMap.get(uid);

if (oldPunishCount == null) {
oldPunishCount = 0;
}

oldPunishCount += 1;
mPunishMap.put(uid, oldPunishCount);

//获取对应状态对象进行响应操作
getPunishState(oldPunishCount).punish(uid, oldPunishCount, this);
}
}

抽象状态角色

1
2
3
4
5
6
7
8
9
10
public interface PunishState {
/**
* 违规处理
*
* @param uid 用户ID
* @param violationCount 违规次数
* @param punishManager 环境角色
*/
public void punish(String uid, int violationCount, PunishManager punishManager);
}

具体状态角色

根据不同的具体状态角色做相应的业务

1
2
3
4
5
6
7
8
public class LowPunishState implements PunishState {

@Override
public void punish(String uid, int violationCount, PunishManager punishManager) {
//违规1~3次,警告制裁
System.out.println("警告制裁");
}
}
1
2
3
4
5
6
7
public class MidPunishState implements PunishState {
@Override
public void punish(String uid, int violationCount, PunishManager punishManager) {
//违规3次以上,封号三天
System.out.println("封号三天");
}
}
1
2
3
4
5
6
7
public class HeightPunishState implements PunishState {
@Override
public void punish(String uid, int violationCount, PunishManager punishManager) {
//违规5次以上,封号一周
System.out.println("封号一周");
}
}
1
2
3
4
5
6
7
public class BlackPunishState implements PunishState {
@Override
public void punish(String uid, int violationCount, PunishManager punishManager) {
//违规10次,永久封号
System.out.println("永久封号");
}
}

入口类

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
PunishManager punishManager = new PunishManager();

for (int i = 1; i <= 10; i++) {
punishManager.punish("Kevin");
}
}
}

运行结果:

优点

  • 封装了转换规则
  • 结构清晰,提高可维护性
  • 不同状态对应的不同行为放到单独类中,方便增加新的状态,只需改变对象状态即可改变对象行为

缺点

  • 增加了类和对象的个数
  • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。