
简介
状态模式
有点类似策略模式
,同样是变化,只是应对变化的方式不一样而已。策略模式面对变化,是采取将变化的部分与不变的部分分离开来,采取封装算法族的方式进行抽象,具体实现只需对不同接口进行组合。而状态模式,变化的部分取决于对象的内部状态,通过当前的状态来决定每个方法的具体实现。
思路
《Head First设计模式》给了一个糖果机的案例,它的运行机制是这样的:首先糖果机有四种状态,分别是没有25分钱
、有25分钱
、售出糖果
、糖果售罄
。同时,导致糖果机状态变化也对应着四个动作,分别是:投入25分钱
、退回25分钱
、转动曲柄
、发放糖果
。糖果机的状态转化关系如下:

如图,糖果机的实现应该包含四个方法,而且每个方法的具体行为跟糖果机当前的状态有关系。比如说,当没有25分钱时是不能退回25分钱和售出糖果的。当转动曲柄的时候,如果没有糖果应该提醒顾客已售空。
实现
在使用状态模式之前,我们的 代码实现 可能是这样的
1 | public class GumballMachine { |
这样子实现也没什么问题,但是随着业务的变更,比如需要有10%的可能会掉下两个糖果,发现上面的代码并没有遵循开闭原则、状态转化埋藏在条件语句中,并不好理解。更重要的是没有将会变化的部分封装,牵一发而动全身,未来很有可能导致隐藏的bug。
重构
在重构之前,我们先梳理一下逻辑:
- 首先,我们定义一个State接口,在这个接口内,糖果机的每个动作都有一个实现的方法。
- 然后,为机器的每个状态实现状态类,这些状态类将负责在对应的状态下进行的行为。
- 最后,我们要摆脱旧的条件代码,取而代之的是,将动作委托到状态类。
类图设计如下:

状态接口定义为
1 | public interface State { |
糖果机的实现
1 | public class StateGumballMachine { |
对应没有25分钱的状态类实现为:
1 | public class NoQuaterState implements State { |
售出糖果的状态实现
1 | public class SoldState implements State { |
从上面 代码实现 很容易看出,每个状态是如何进行转化
的,而且如果需要再添加一种状态的话,也是很容易进行扩展
的。总而言之言之,状态模式提升了代码的可读性
和可扩展性
。
那么,现在回到刚才那个问题:
需要添加可能会一下出两颗糖果的可能,代码应该怎么实现呢?
定义
书上定义为:
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
类关系图为:
