状态模式(学习笔记2021.09.09)
前言:
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
**意图:**允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
**主要解决:**对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
**何时使用:**代码中包含大量与对象状态有关的条件语句。
**如何解决:**将各种具体的状态类抽象出来。
**关键代码:**通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。
应用实例: 1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。 2、曾侯乙编钟中,‘钟是抽象接口’,'钟A’等是具体状态,'曾侯乙编钟’是具体环境(Context)。
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。
**注意事项:**在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
前提条件:
下面使用状态模式解决
状态模式
解决前提问题
定义对应状态的行为规范接口
/**
* 定义对应状态需要做的行为规范接口
*/
public interface State {
// 投钱
void insertQuarter();
// 退钱
void ejectQuarter();
// 转动曲柄
void turnCrank();
// 发放糖果
void dispense();
// 补充
void refill();
}
改造糖果机
/** 糖果机 **/
public class GumballMachine {
/**
* 每种状态对应需要操作的行为
*/
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
/**
* 真实获取到然后执行的对应状态的操作行为
**/
State state;
int count = 0;
public GumballMachine(int numberGumballs) {
// 初始化每一种状态对应需要操作的行为
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
this.count = numberGumballs;
// 当糖果数量大于0, 转换真实获取到然后执行的对应的没有投钱操作行为
if (numberGumballs > 0) {
state = noQuarterState;
} else {
// 当糖果数量小于0, 转换真实获取到然后执行的对应的没有货物操作行为
state = soldOutState;
}
}
/** 执行 对应真实操作状态行为 **/
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
}
public void releaseBall() {
System.out.println("一个口香糖球从缝里滚出来。。。");
if (count != 0) {
count--;
}
}
public int getCount() {
return count;
}
public void refill(int count) {
this.count += count;
System.out.println("口香糖机刚刚被重新装满;这是新的计数: " + this.count);
state.refill();
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
@Override
public String toString() {
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004");
result.append("\nInventory: " + count + " gumball");
if (count != 1) {
result.append("s");
}
result.append("\n");
result.append("Machine is " + state + "\n");
return result.toString();
}
}
然后从没有投钱的状态行为开始
也是默认行为动作状态, 投钱后就转换了糖果机的行为动作状态
// 没有给钱的状态, 需要操作的行为
public class NoQuarterState implements State {
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
// 当用户插入了钱后, 转换口香糖机的状态为, 真实投钱的状态行为实现
@Override
public void insertQuarter() {
System.out.println("你进行了投钱");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
System.out.println("你还没有插入一个25美分的硬币, 无钱可退");
}
@Override
public void turnCrank() {
System.out.println("你转动了曲柄, 但是没有硬币");
}
@Override
public void dispense() {
System.out.println("你需要先付钱");
}
@Override
public void refill() {
}
public String toString() {
return "waiting for quarter";
}
}
然后是投钱的状态行为
// 有投钱状态行为动作
public class HasQuarterState implements State {
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("您不能再插入一个25美分的硬币");
}
@Override
public void ejectQuarter() {
System.out.println("进行退款了");
// 将糖果机状态转换回没有投钱的状态行为
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
@Override
public void turnCrank() {
System.out.println("你转动了曲柄");
// 设置糖果机转换发放糖果状态行为
gumballMachine.setState(gumballMachine.getSoldState());
}
// 这里状态不做发放商品行为动作
@Override
public void dispense() {
System.out.println("没有分发口香糖");
}
@Override
public void refill() {
}
@Override
public String toString() {
return "等待曲柄转动";
}
}
然后是发放糖果的状态行为
// 发放状态行为动作
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
// insertQuarter ejectQuarter turnCrank 都不是这个状态行为的合适动作
@Override
public void insertQuarter() {
System.out.println("请稍等,我们已经在给你口香糖了");
}
@Override
public void ejectQuarter() {
System.out.println("对不起,你已经转动曲柄了");
}
@Override
public void turnCrank() {
System.out.println("转两圈也不会让你再有口香糖!");
}
// 这个状态行为真正需要执行的动作是发放糖果
@Override
public void dispense() {
gumballMachine.releaseBall();
// 当糖果数量大于0, 转换真实获取到然后执行的对应的没有投钱操作行为
if (gumballMachine.getCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
// 当糖果数量小于0, 转换真实获取到然后执行的对应的没有货物操作行为
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
@Override
public void refill() {
}
public String toString() {
return "dispensing a gumball";
}
}
然后是售罄状态的状态行为
// 售罄状态的状态行为
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("你不能投钱,机器已经卖完了");
}
@Override
public void ejectQuarter() {
System.out.println("你不能退款,你还没有投钱");
}
@Override
public void turnCrank() {
System.out.println("你转身了,但是没有口香糖");
}
@Override
public void dispense() {
System.out.println("没有分发口香糖");
}
@Override
public void refill() {
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public String toString() {
return "sold out";
}
}
进行测试
@Test
public void applicationTest() throws Exception {
// 装了2颗糖果
GumballMachine gumballMachine = new GumballMachine(2);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.dispense();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.dispense();
gumballMachine.refill(2);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.dispense();
}
//------------------------------------------
你进行了投钱
你转动了曲柄
一个口香糖球从缝里滚出来。。。
你不能投钱,机器已经卖完了
你转身了,但是没有口香糖
没有分发口香糖
口香糖机刚刚被重新装满;这是新的计数: 2
你进行了投钱
你转动了曲柄
一个口香糖球从缝里滚出来。。。
从上面结果可以看出, 糖果机里面的
state
状态不断的变化, 执行对应状态方法时候都根据变化的状态执行对应的行为用户只感知到了与糖果机的交互, 并不知道糖果机里面的各种状态转换, 执行状态行为。
1