背景
在开发过程中,对象会有根据不同的情况做出不同的行为,而影响行为的属性称之为状态属性,这个对象也就是状态对象。
我们在编码时,可能会使用if-else
来做状态判断。例如我有一个活动类Activity
,这个类里面有一个活动开始字段,活动结束字段,还有一个状态字段,现在我们要来判断活动的状态:未开始、进行中、已结束。示例代码如下:
当状态很多,那么程序会变得很复杂。如果有新的状态增加,就要添加新的if语句,违背设计原则中的开闭原则。
所以我们采用状态模式来解决这个问题,我们将对象状态的判断逻辑抽离出来,放到一个状态类中,这样将原本复杂的逻辑判断简单化。
什么是状态模式
“Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。) ”
状态模式由下面3个要素构成
- 环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为。
结构图如下:
状态模式
示例代码
State
代码语言:javascript复制public abstract class State {
public abstract void handle(Context context);
}
Context
代码语言:javascript复制public class Context {
private State state;
//定义环境类的初始状态
public Context() {
this.state = new ConcreteStateA();
}
//设置新状态
public void setState(State state) {
this.state = state;
}
//读取状态
public State getState() {
return (state);
}
//对请求做处理
public void handle() {
state.handle(this);
}
}
ConcreteState
代码语言:javascript复制public class ConcreteStateA extends State{
public void handle(Context context)
{
System.out.println("当前状态是 A.");
context.setState(new ConcreteStateB());
}
}
public class ConcreteStateB extends State{
public void handle(Context context)
{
System.out.println("当前状态是 B.");
context.setState(new ConcreteStateA());
}
}
测试代码
代码语言:javascript复制@Test
public void test() {
Context context=new Context(); //创建环境
context.handle(); //处理请求
context.handle();
context.handle();
context.handle();
}
测试结果:
代码语言:javascript复制当前状态是 A.
当前状态是 B.
当前状态是 A.
当前状态是 B.
我们每调用一次handle()
,就改变一次状态。
关于状态模式的思考
经过上面的讲解,你肯定知道状态模式的使用场景了吧。当对象的行为取决于它的状态,并且在运行时会根据他的状态来改变它的行为。我们就可以考虑使用状态模式。
状态模式的好处在于,我可以避免写过多的switch-case
或者if-else
语句。这样避免了程序的过分复杂。
从上面的代码我们可以知道,开发者无需知道状态的改变细节。这种良好的封装性是我们的代码更优雅。
但是你是否意识到,如果状态类型太多,那么就要写很多的状态子类,这样也会加大系统的管理难度。使用状态模式的时候要考虑这方面的问题。
关于消除if-else
,我之前也写过文章使用枚举和策略模式消除,现在又多了一种方法