状态模式,听过的人可能不是很多,它不在我们熟知的 23 中设计模式之内。但它归属与行为型模式中的一种。今天我们一起来学习学习状态模式!
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。通常我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
状态模式最大的优点是:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决的问题是:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
使用场景:代码中包含大量与对象状态有关的条件语句。
如何使用,下面我们通过一个订单的状态来看一个实例。
在电商系统,购物过程中,订单一般有这几种状态,待付款,待出库,待评价,完成。不同的状态下,用户看到的提示信息是不同的(行为不同)。
根据上面的类图,我们先来实现一个抽象状态接口。
<span style="color:#f8f8d4">public interface OrderState{
void action(Context context);
void doPrint();
}</span>
待付款实现类。
<span style="color:#f8f8d4">public class PendingPaymentState implements OrderState{
@Override
public void action(Context context){
context.setOrderState(new WaitOutStockState());
}
@Override
public void doPrint(){
System.out.println("处理待付款状态的逻辑");
}
}</span>
待出库状态。
<span style="color:#f8f8d4">public class WaitOutStockState implements OrderState{
@Override
public void action(Context context){
context.setOrderState(new PendingDeliveryOrder());
}
@Override
public void doPrint(){
System.out.println("等待出库状态");
}
}</span>
待收货状态。
<span style="color:#f8f8d4">public class PendingDeliveryOrder implements OrderState{
@Override
public void action(Context context){
context.setOrderState(new PendingEvaluation());
}
@Override public void doPrint(){
System.out.println("待收货状态");
}
}</span>
待评价状态。
<span style="color:#f8f8d4">public class PendingEvaluation implements OrderState{
@Override
public void action(Context context){
context.setOrderState(new CompletedOrder());
}
@Override
public void doPrint(){
System.out.println("待评价状态");
}
}</span>
订单完成状态。
<span style="color:#f8f8d4">public class CompletedOrder implements OrderState{
@Override
public void action(Context context){
context.setOrderState(null);
}
@Override
public void doPrint(){
System.out.println("订单完成状态");
}
}</span>
Context 类,订单状态变化引起行为变化。
<span style="color:#f8f8d4">public class Context{
private OrderState orderState;
public Context(OrderState orderState){
this.orderState = orderState;
}
public void setOrderState(OrderState orderState){
this.orderState = orderState;
}
public void action(){
this.orderState.action(this);
}
public void printInfo(){
if (this.orderState != null){
this.orderState.doPrint();
}
}
}</span>
下面使用 XttblogOrderDemo 类,模拟一下生产环境中的使用场景。
<span style="color:#f8f8d4">public class XttblogOrderDemo{
public static void main(String[] args){
OrderState orderState = new PendingPaymentState();
Context context = new Context(orderState);
context.printInfo();
//用户付款
context.action();
context.printInfo();
//商家出库发货
context.action();
context.printInfo();
//用户签收
context.action();
context.printInfo();
//用户评价
context.action();
context.printInfo();
}
}</span>
总结下来,状态模式中涉及到 3 个角色。
- 环境(Context)角色,也成上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
- 抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。
- 具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
状态模式和策略模式的结构是比较相似的,但是两者却存在较大区别。策略模式是对算法或者策略建模,不涉及算法之间的切换。而状态模式是对状态进行建模,涉及状态之间的切换,其实就是状态机的模型,每种状态对应不同的处理逻辑。
状态模式在实际工作中非常的常用,比如:投票应用。一个在线投票系统的应用,要实现控制同一个用户只能投一票,如果一个用户反复投票,而且投票次数超过5次,则判定为恶意刷票,要取消该用户投票的资格,当然同时也要取消他所投的票;如果一个用户的投票次数超过8次,将进入黑名单,禁止再登录和使用系统。要使用状态模式实现,首先需要把投票过程的各种状态定义出来,根据以上描述大致分为四种状态:正常投票、反复投票、恶意刷票、进入黑名单。
叫号系统,取号,排队等待,办理业务,评价等。在比如游戏中的不同动作等。