状态模式
定义
状态模式允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
状态模式和策略模式的区别:
策略模式允许在运行中使用不同的算法/策略,是在外部(客户主动使用)将具体的策略类传入;
状态模式允许在运行时,通过改变类的内部状态的方式,改变其行为。状态的改变是在类内部实现的,客户并不感知。
UML类图
使用场景
1.行为随状态改变而改变的场景。
2.条件、分支语句的代替者。
实例
例子:一个饮料机,有5类状态:售罄、已投入硬币、没有投入硬币、正在售出、赢家(一枚硬币换2瓶饮料)
饮料机
// 饮料机
public class DrinkMachine {
// 售罄状态
State soldOutState;
// 没有硬币状态
State noQuarterState;
// 有硬币状态
State hasQuarterState;
// 售出状态
State soldState;
// 赢家状态
State winnerState;
// 当前状态
State currentState = soldOutState;
// 饮料机中饮料个数
int drinkNumber = 0;
public DrinkMachine(int drinkNumber) {
this.drinkNumber = drinkNumber;
soldState = new SoldState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
if (drinkNumber > 0) {
// 饮料数量大于0,当前状态变为“没有投入硬币”状态
currentState = noQuarterState;
}
}
public void setState(State state) {
this.currentState = state;
}
// 投入硬币
public void insertQuarter() {
currentState.insertQuarter();
}
// 取出硬币
public void ejectQuarter() {
currentState.ejectQuarter();
}
// 按下按钮
public void pushTheButton() {
currentState.PushTheButton();
}
// 释放饮料
public void outDrink() {
currentState.outDrink();
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
public State getCurrentState() {
return currentState;
}
public int getDrinkNumber() {
return drinkNumber;
}
public State getWinnerState() {
return winnerState;
}
public void setDrinkNumber(int drinkNumber) {
this.drinkNumber = drinkNumber;
}
@Override
public String toString() {
return "DrinkMachine{" +
"soldOutState=" + soldOutState +
", noQuarterState=" + noQuarterState +
", hasQuarterState=" + hasQuarterState +
", soldState=" + soldState +
", currentState=" + currentState +
", drinkNumber=" + drinkNumber +
'}';
}
}
状态接口
// 状态接口中定义了饮料机所有可以‘改变状态‘的动作
public interface State {
// 投入25分硬币
void insertQuarter();
// 退出25分硬币
void ejectQuarter();
// 按下按钮
void PushTheButton();
// 发放饮料
void outDrink();
}
// 饮料机处于没有硬币的状态
public class NoQuarterState implements State{
DrinkMachine drinkMachine;
public NoQuarterState(DrinkMachine drinkMachine){
this.drinkMachine = drinkMachine;
}
// 投入25分硬币
@Override
public void insertQuarter() {
System.out.println("你投入了一枚硬币...");
// 改变饮料机状态为”有一枚硬币“状态
drinkMachine.setState(drinkMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
System.out.println("未投入硬币...");
}
@Override
public void PushTheButton() {
System.out.println("未投入硬币...");
}
@Override
public void outDrink() {
System.out.println("未投入硬币...");
}
}
// 饮料机处于有硬币的状态
public class HasQuarterState implements State {
DrinkMachine drinkMachine;
// 赢家随机数
Random winnerRandom = new Random(System.currentTimeMillis());
public HasQuarterState(DrinkMachine drinkMachine) {
this.drinkMachine = drinkMachine;
}
@Override
public void insertQuarter() {
System.out.println("饮料机中已经存在硬币,不允许再次投入硬币...");
}
@Override
public void ejectQuarter() {
System.out.println("正在取出硬币...");
// 改变饮料机状态为”没有投入硬币“
drinkMachine.setState(drinkMachine.getNoQuarterState());
}
@Override
public void PushTheButton() {
System.out.println("正在按下饮料机按钮...");
if (winnerRandom.nextInt(2) == 0) {
// 进入赢家模式
// 可售出2瓶饮料
drinkMachine.setState(drinkMachine.getWinnerState());
} else {
// 普通模式
// 改变饮料机状态为”正在售出“
drinkMachine.setState(drinkMachine.getSoldState());
}
}
@Override
public void outDrink() {
System.out.println("请按下饮料机按钮...");
}
}
// 饮料机处于正在售出状态
public class SoldState implements State {
DrinkMachine drinkMachine;
public SoldState(DrinkMachine drinkMachine) {
this.drinkMachine = drinkMachine;
}
@Override
public void insertQuarter() {
System.out.println("饮料机正在售出饮料,不允许投币...");
}
@Override
public void ejectQuarter() {
System.out.println("饮料机正在售出饮料,不允许取币...");
}
@Override
public void PushTheButton() {
System.out.println("饮料机正在售出饮料,不允许按下按钮...");
}
@Override
public void outDrink() {
if (drinkMachine.getDrinkNumber() > 0) {
// 饮料数量 > 0
System.out.println("正在售出饮料...");
drinkMachine.setDrinkNumber(drinkMachine.getDrinkNumber() - 1);
drinkMachine.setState(drinkMachine.getNoQuarterState());
} else {
System.out.println("饮料机中饮料已经售罄...");
drinkMachine.setState(drinkMachine.getSoldOutState());
}
}
}
// “赢家”状态(在“有一个硬币”状态时按下按钮后才能进入赢家状态)
// “赢家”状态下,可以发放两瓶饮料
public class WinnerState implements State {
DrinkMachine drinkMachine;
public WinnerState(DrinkMachine drinkMachine) {
this.drinkMachine = drinkMachine;
}
@Override
public void insertQuarter() {
System.out.println("当前不可投币...");
}
@Override
public void ejectQuarter() {
System.out.println("当前不可取币...");
}
@Override
public void PushTheButton() {
System.out.println("当前不可按下按钮...");
}
@Override
public void outDrink() {
// 饮料机当前饮料数量
int currentDrinkNum = drinkMachine.getDrinkNumber();
if (currentDrinkNum >= 2) {
// 满足赢家状态所需要的两瓶饮料时
System.out.println("赢家模式:正在发放2瓶饮料...");
drinkMachine.setDrinkNumber(currentDrinkNum - 2);
drinkMachine.setState(drinkMachine.getNoQuarterState());
} else {
// 不满足,则进入普通的售出状态
drinkMachine.setState(drinkMachine.getSoldState());
drinkMachine.outDrink();
}
}
}
// 饮料机处于售罄状态
public class SoldOutState implements State{
DrinkMachine drinkMachine;
public SoldOutState(DrinkMachine drinkMachine){
this.drinkMachine = drinkMachine;
}
@Override
public void insertQuarter() {
System.out.println("饮料机中没有饮料...");
}
@Override
public void ejectQuarter() {
System.out.println("饮料机中没有饮料...");
}
@Override
public void PushTheButton() {
System.out.println("饮料机中没有饮料...");
}
@Override
public void outDrink() {
System.out.println("饮料机中没有饮料...");
}
}
测试代码:
public class Test {
public static void main(String[] args) {
DrinkMachine drinkMachine = new DrinkMachine(20);
System.out.println(drinkMachine);
// 1.
// 投币
drinkMachine.insertQuarter();
// 按下按钮
drinkMachine.pushTheButton();
// 滚出饮料
drinkMachine.outDrink();
System.out.println(drinkMachine);
// 2.
// 投币
drinkMachine.insertQuarter();
// 取币
drinkMachine.ejectQuarter();
System.out.println(drinkMachine);
// 3.错误示范
// 按下按钮
drinkMachine.pushTheButton();
System.out.println(drinkMachine);
//4. 测试赢家模式
System.out.println("测试赢家模式中...");
for (int i = 0; i < 10; i++) {
drinkMachine.insertQuarter();
drinkMachine.pushTheButton();
drinkMachine.outDrink();
}
}
}
优缺点
优点:
1.封装了转换规则。
2.枚举可能的状态,在枚举状态之前需要确定状态种类。
3.将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4.允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5.可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
缺点:
1.状态模式的使用必然会增加系统类和对象的个数。
2.状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3.状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
工作中的应用
暂无
设计原则工具箱
1.找出应用中可能需要变化的部分,把它们独立出来,不要和那些不需要变化的代码混合在一起
2.针对接口(超类型)编程而不是针对具体的实现编程
3.多用组合少用继承
4.为了交互对象之间的松耦合设计而努力
5.类应该对扩展开放,对修改关闭
6.依赖抽象,不要依赖具体类。(即不要让高层组件依赖低层组件,而且不管高层组件还是低层组件都应该依赖于抽象)
7.好莱坞原则:别调用我们,我们会调用你。(在好莱坞原则下,我们允许底层组件(具体实现类)将自己挂钩到系统上。但是由高层组件(抽象基类)决定什么时候和怎么去使用这些底层组件。换句话说,高层组件对待底层组件的方式是:“别调用我们,我们会调用你”)
8.单一职责原则:一个类应该只有一个引起变化的原因