第二十二章:状态模式
一、模式简介
一个事物有不同的状态,比如人有高兴、悲伤、生气等状态。人处于这些不同的状态会有不同的行为,如高兴时会唱歌、生气时会跺脚等等。而且这些状态会随着行为的发生而相互转换,如生气时跺脚人的状态就由生气转变为冷静。
如果我们在一个方法中使用 if 语句来描述这些状态的改变,势必会有大量 if 的嵌套,而且后续增加新的状态还要修改代码不符合开闭原则。
所以我们使用状态模式来代替 if 语句
基本类图:
- State:抽象状态类,里面声明了各个行为
- CurrentStateA、CurrentStateB:具体状态类,根据具体的状态,行为有不同的实现
- Subject:状态的拥有者,currentState 指明了这个主体当前处于的状态
二、具体案例
我们使用尚硅谷的抽奖案例,因为一个比较好又比较容易实现的案例暂时没想到╮(╯-╰)╭
状态转移图:
我们根据上面的状态转移图来编程:
抽象 State 类
abstract public class State {
abstract public void deduceScore();
abstract public boolean raffle();
abstract public void dispensePrize();
}
具体状态类
不能抽奖状态
public class NoRaffleState extends State {
private Activity activity;
public NoRaffleState(Activity activity) {
this.activity = activity;
}
@Override
public void deduceScore() {
if (this.activity.deduceScore()) {
this.activity.setState(this.activity.getCanRaffleState());
} else {
System.out.println("积分不足,请充值");
}
}
@Override
public boolean raffle() {
System.out.println("需要扣除积分,再抽奖");
return false;
}
@Override
public void dispensePrize() {
System.out.println("需要先抽奖");
}
}
能抽奖状态
public class CanRaffleState extends State {
private Activity activity;
public CanRaffleState(Activity activity) {
this.activity = activity;
}
@Override
public void deduceScore() {
System.out.println("已经扣过积分了,请抽奖");
}
@Override
public boolean raffle() {
Random random = new Random(new Date().getTime());
int num = random.nextInt(10);
if (num == 0) {
activity.setState(activity.getDispenseState());
return true;
} else {
activity.setState(activity.getNoRaffleState());
return false;
}
}
@Override
public void dispensePrize() {
System.out.println("需要先抽奖");
}
}
发奖状态
public class DispenseState extends State {
private Activity activity;
public DispenseState(Activity activity) {
this.activity = activity;
}
@Override
public void deduceScore() {
System.out.println("已经中奖了了,需要发奖");
}
@Override
public boolean raffle() {
System.out.println("已经中奖了了,需要发奖");
return false;
}
@Override
public void dispensePrize() {
this.activity.subtractCount();
if (this.activity.getCount() > 0) {
this.activity.setState(this.activity.getNoRaffleState());
} else {
this.activity.setState(this.activity.getDispenseOutState());
}
System.out.println("恭喜中奖了");
}
}
没有奖品状态
public class DispenseOutState extends State {
private Activity activity;
public DispenseOutState(Activity activity) {
this.activity = activity;
}
@Override
public void deduceScore() {
System.out.println("没有奖品了,无法抽奖");
}
@Override
public boolean raffle() {
System.out.println("没有奖品了,无法抽奖");
return false;
}
@Override
public void dispensePrize() {
System.out.println("没有奖品了,无法抽奖");
}
}
状态拥有者
public class Activity {
// 也可以使用单例模式或享元模式代替
private State noRaffleState = new NoRaffleState(this);
private State canRaffleState = new CanRaffleState(this);
private State dispenseState = new DispenseState(this);
private State dispenseOutState = new DispenseOutState(this);
private State state;
private int score;
private int count;
public Activity(int score, int count) {
this.score = score;
this.count = count;
this.state = getNoRaffleState();
}
public void setState(State state) {
this.state = state;
}
public State getNoRaffleState() {
return noRaffleState;
}
public State getCanRaffleState() {
return canRaffleState;
}
public State getDispenseState() {
return dispenseState;
}
public State getDispenseOutState() {
return dispenseOutState;
}
public int getScore() {
return score;
}
public boolean deduceScore() {
if (this.score >= 50) {
this.score -= 50;
return true;
}
return false;
}
public int getCount() {
return count;
}
public void subtractCount() {
this.count--;
}
public void run() {
this.state.deduceScore();
if (this.state.raffle()) {
this.state.dispensePrize();
} else {
System.out.println("很遗憾,没有抽中");
}
}
}
测试抽奖
public class StateTest {
public static void main(String[] args) {
Activity activity = new Activity(300, 5);
for (int i = 0; i < 10; i++) {
activity.run();
}
}
}
/**********
很遗憾,没有抽中
很遗憾,没有抽中
很遗憾,没有抽中
很遗憾,没有抽中
很遗憾,没有抽中
很遗憾,没有抽中
积分不足,请充值
需要扣除积分,再抽奖
很遗憾,没有抽中
积分不足,请充值
需要扣除积分,再抽奖
很遗憾,没有抽中
积分不足,请充值
需要扣除积分,再抽奖
很遗憾,没有抽中
积分不足,请充值
需要扣除积分,再抽奖
很遗憾,没有抽中
**********/
三、模式总结
优点:
- 可读性强
- 方便维护,不用 if 语句来判断现在处于哪个状态
缺点:
- 有很多状态类