[设计模式] 行为型 | 8.状态模式
1.模式定义:
- 状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
- 其别名为状态对象(Objects for States),状态模式是一种对象行为型模式
2. 模式结构:
状态模式包含如下角色:
- Context: 环境类
- State: 抽象状态类
- ConcreteState: 具体状态类

3.模式分析:
- 用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
- 将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化
- 对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理
状态转换的实现:
(1) 统一由环境类来负责状态之间的转换,环境类充当状态管理器(State Manager)角色
……
public void changeState()
{
//判断属性值,根据属性值进行状态转换
if (value == 0)
{
this.setState(new ConcreteStateA());
}
else if (value == 1)
{
this.setState(new ConcreteStateB());
}
......
}
……
(2) 由具体状态类来负责状态之间的转换,可以在具体状态类的业务方法中判断环境类的某些属性值,再根据情况为环境类设置新的状态对象,实现状态转换
……
public void changeState(Context ctx) {
//根据环境对象中的属性值进行状态转换
if (ctx.getValue() == 1) {
ctx.setState(new ConcreteStateB());
}
else if (ctx.getValue() == 2) {
ctx.setState(new ConcreteStateC());
}
......
}
……
4. 模式实例
-
论坛用户等级:实例说明
在某论坛系统中,用户可以发表留言,发表留言将增加积分;用户也可以回复留言,回复留言也将增加积分;用户还可以下载文件,下载文件将扣除积分。该系统用户分为三个等级,分别是新手、高手和专家,这三个等级对应三种不同的状态,这三种状态分别定义如下:
(1) 如果积分小于100分,则为新手状态,用户可以发表留言、回复留言,但是不能下载文件。如果积分大于等于1000分,则转换为专家状态;如果积分大于等于100分,则转换为高手状态。
(2) 如果积分大于等于100分但小于1000分,则为高手状态,用户可以发表留言、回复留言,还可以下载文件,而且用户在发表留言时可以获取双倍积分。如果积分小于100分,则转换为新手状态;如果积分大于等于1000分,则转换为专家状态;如果下载文件后积分小于0,则不能下载该文件。
(3) 如果积分大于等于1000分,则为专家状态,用户可以发表留言、回复留言和下载文件,用户除了在发表留言时可以获取双倍积分外,下载文件只扣除所需积分的一半。如果积分小于100分,则转换为新手状态;如果积分小于1000分,但大于等于100,则转换为高手状态;如果下载文件后积分小于0,则不能下载该文件。 -
论坛用户等级:参考类图

5.模式优缺点:
优点
-
封装了状态的转换规则,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中
-
将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为
-
允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,可以避免使用庞大的条件语句来
将业务方法和状态转换代码交织在一起
-
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
缺点
- 会增加系统中类和对象的个数,导致系统运行开销增大
- 结构与实现都较为复杂,如果使用不当将导致程序结构和代码混乱,增加系统设计的难度
- 对开闭原则的支持并不太好,增加新的状态类需要修改负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需要修改对应类的源代码
6.使用场景:
- 对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化
- 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强