基本介绍
状态模式(State Pattern)主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题,类的行为是基于它的状态改变的。
模式结构
Context(环境角色):用于维护 State 实例,这个实例定义当前状态
State(抽象状态):定义一个接口以封装与 Context 的一个特定状态相关的行为
ConcreteState(具体状态):每一子类实现一个与 Context 的一个状态相关的行为
举例说明
我们以抽奖的场景作为例子,流程如下:
定义一个抽象状态:State
public interface State {
RuntimeException EXCEPTION = new RuntimeException("不能执行此操作");
/**
* 报名
*/
default void signUp() {
throw EXCEPTION;
}
/**
* 抽奖
*/
default boolean raffle() {
throw EXCEPTION;
}
/**
* 发奖品
*/
default void sendPrize() {
throw EXCEPTION;
}
}
具体状态1:不能抽奖
public class NotRaffleState implements State {
private RaffleActivity activity;
public NotRaffleState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void signUp() {
//如果没有奖品,直接进入无奖品状态
if (!activity.hasPrize()) {
activity.setState(activity.getNoPrizeState());
return;
}
System.out.println("报名成功,可以抽奖了");
activity.setState(activity.getCanRaffleState());
}
}
具体状态2:可以抽奖
public class CanRaffleState implements State {
private RaffleActivity activity;
public CanRaffleState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public boolean raffle() {
//模拟五分之一的中奖几率
int n = new Random().nextInt(5);
if (n == 0) {
//抽中,进入发放奖品的状态
System.out.println("恭喜你,抽中了");
activity.setState(activity.getSendPrizeState());
return true;
} else {
//未抽中,回到初始状态
System.out.println("很遗憾,没有抽中");
activity.setState(activity.getNotRaffleState());
return false;
}
}
}
具体状态3:发放奖品
public class SendPrizeState implements State {
private RaffleActivity activity;
public SendPrizeState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void sendPrize() {
int count = activity.getCount();
//没有奖品了
if (count <= 0) {
System.out.println("奖品没有了,下次再来吧");
activity.setState(activity.getNoPrizeState());
} else {
activity.setCount(--count);
System.out.println("奖品已发送");
activity.setState(activity.getNotRaffleState());
}
}
}
具体状态4:奖品领完
public class NoPrizeState implements State {
private RaffleActivity activity;
public NoPrizeState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void signUp() {
System.out.println("奖品没有了,下次再来吧");
System.exit(0);
}
@Override
public boolean raffle() {
System.out.println("奖品没有了,下次再来吧");
System.exit(0);
return false;
}
@Override
public void sendPrize() {
System.out.println("奖品没有了,下次再来吧");
System.exit(0);
}
}
环境角色:抽奖活动
public class RaffleActivity {
private State state;
private int count;
private State notRaffleState = new NotRaffleState(this);
private State canRaffleState = new CanRaffleState(this);
private State sendPrizeState = new SendPrizeState(this);
private State noPrizeState = new NoPrizeState(this);
public RaffleActivity(int count) {
this.state = getNotRaffleState();
this.count = count;
}
public void signUp() {
state.signUp();
}
public void raffle() {
if (state.raffle()) {
state.sendPrize();
}
}
public State getState() {
return state;
}
public boolean hasPrize(){
return count > 0;
}
//省略getter、setter
}
客户端
public class Client {
@Test
public void test(){
RaffleActivity activity = new RaffleActivity(1);
for (int i = 0; i < 5; i++) {
System.out.println("第" + (i + 1) + "次抽奖");
activity.signUp();
activity.raffle();
System.out.println("------------------");
}
}
}
运行结果
第1次抽奖
报名成功,可以抽奖了
很遗憾,没有抽中
------------------
第2次抽奖
报名成功,可以抽奖了
恭喜你,抽中了
奖品已发送
------------------
第3次抽奖
奖品没有了,下次再来吧
模式分析
优点:
- 代码具有较强的可读性。状态模式将每个状态的行为封装到对应的一个类中
- 方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
缺点:
- 会产生很多类。每个状态都对应一个类,当状态过多时,维护难度变大
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
- 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
适用环境:
-
行为随状态改变而改变的场景
-
条件、分支语句的代替者