1.定义
允许对象在内部状态改变时改变它的行为,对象看起来好像是修改了它的类。
2.类图
3.状态模式与策略模式区别
- 状态模式与策略模式拥有相同的类图,都会事先定义好一组类(状态或行为)
- 状态模式会事先定义一组状态执行的顺序,类经过不同的状态时会有不同的行为,即通过状态的改变来控制类的行为
- 策略模式不会预先定义状态转换的顺序,通常是通过组合和委托来拥有不同的行为或算法
4.测试代码
此处使用自动售货机的例子作为测试代码
自动售货机有三个状态和三个动作:
- 状态:已投币状态、未投币状态、商品售罄状态
- 动作:投币,退币,点击出货
4.1.测试代码类图
4.2.代码
4.2.1.状态接口
/**
* 自动售货机状态接口
*/
public interface State {
/**
* 投币
*/
void insertMoney();
/**
* 退币
*/
void deleteMoney();
/**
* 出货
*/
void soldGoods();
}
4.2.2.已投币状态类
/**
* 已投币状态
*/
public class HasMoney implements State {
GoodsMachine goodsMachine;
HasMoney(GoodsMachine goodsMachine) {
this.goodsMachine = goodsMachine;
}
@Override
public void insertMoney() {
System.out.println("已投币状态,不能重复投币");
}
@Override
public void deleteMoney() {
System.out.println("退币成功");
goodsMachine.setState(goodsMachine.getNoMoney());
}
@Override
public void soldGoods() {
System.out.println("出货了,请收好。。。。");
goodsMachine.setCount(goodsMachine.getCount()-1);
if(goodsMachine.getCount()>0){
System.out.println("出货完毕,库存大于0,设置为未投币状态");
goodsMachine.setState(goodsMachine.getNoMoney());
}else {
System.out.println("出货完毕,库存为0,设置为售罄状态");
goodsMachine.setState(goodsMachine.getSoldOutGoods());
}
}
}
4.2.3.未投币状态类
/**
* 未投币状态
*/
public class NoMoney implements State {
GoodsMachine goodsMachine;
NoMoney(GoodsMachine goodsMachine) {
this.goodsMachine = goodsMachine;
}
@Override
public void insertMoney() {
System.out.println("投币成功");
goodsMachine.setState(goodsMachine.getHasMoney());
}
@Override
public void deleteMoney() {
System.out.println("未投币状态,不能退币");
}
@Override
public void soldGoods() {
System.out.println("未投币状态,不能出货");
}
}
4.2.4.售罄状态
/**
* 售罄状态
*/
public class SoldOutGoods implements State {
GoodsMachine goodsMachine;
SoldOutGoods(GoodsMachine goodsMachine) {
this.goodsMachine = goodsMachine;
}
@Override
public void insertMoney() {
System.out.println("售罄状态,不能投币");
}
@Override
public void deleteMoney() {
System.out.println("售罄状态,不能退币");
}
@Override
public void soldGoods() {
System.out.println("售罄状态,不能出货");
}
}
4.2.5.售货机类
/**
* 自动售货机类
* 内部持有一个状态,可以流转为所有状态
*/
public class GoodsMachine {
private State hasMoney;
private State noMoney;
private State soldOutGoods;
//售货机初始商品为0
private int count = 0;
//售货机初始为售罄状态
private State state = soldOutGoods;
GoodsMachine(int count) {
hasMoney = new HasMoney(this);
noMoney = new NoMoney(this);
soldOutGoods = new SoldOutGoods(this);
this.count = count;
//商品大于0,将状态初始化为未投币状态
if (count > 0) {
this.state = noMoney;
}
}
//投币
void insertMoney() {
state.insertMoney();
}
//点击出货
void soldGoods() {
state.soldGoods();
}
//以下为get、set方法
public State getHasMoney() {return hasMoney;}
public void setHasMoney(State hasMoney) {this.hasMoney = hasMoney;}
public State getNoMoney() {return noMoney;}
public void setNoMoney(State noMoney) {this.noMoney = noMoney;}
public State getSoldOutGoods() {return soldOutGoods;}
public void setSoldOutGoods(State soldOutGoods) {this.soldOutGoods = soldOutGoods; }
public State getState() {return state;}
public void setState(State state) {this.state = state;}
public int getCount() {return count;}
public void setCount(int count) {this.count = count;}
}
4.2.6.测试入口类
public class InitMain {
public static void main(String[] args) {
/**
* 测试自动售货机代码
* 有三个状态:已投币状态、未投币状态、商品售罄状态
* 有三个动作:投币,退币,点击出货
*/
//创建一个有三个商品的自动售货机
GoodsMachine goodsMachine = new GoodsMachine(3);
goodsMachine.insertMoney();//投币
goodsMachine.soldGoods();//点击出货
goodsMachine.insertMoney();//投币
goodsMachine.soldGoods();//点击出货
goodsMachine.insertMoney();//投币
goodsMachine.soldGoods();//点击出货
goodsMachine.insertMoney();//投币
}
}
4.2.7.测试输出及说明
此文中只处理了商品从创建售货机到售罄的状态,没有处理售罄之后重新装入商品的状态,测试代码输出如下:
投币成功
出货了,请收好。。。。
出货完毕,库存大于0,设置为未投币状态
投币成功
出货了,请收好。。。。
出货完毕,库存大于0,设置为未投币状态
投币成功
出货了,请收好。。。。
出货完毕,库存为0,设置为售罄状态
售罄状态,不能投币
5.总结
预先定义一组状态转换(状态流转顺序),通过状态的改变来控制类的行为。至于状态的转换可以在具体状态类中,也可以在容器类中。
如果状态流转放在具体状态类中,则会在状态类之间增加了依赖,如果状态流转放在容器类中,则是尽可能的将状态类之间的依赖降至最低。做这个决策则相当于对开闭原则做决策(对哪个类修改封闭)。
优点:
- 枚举了所有的状态,每个状态都对应一个类。
- 可以方便的增加新的状态,改变对象状态即可改变类的行为。
- 通过状态转换逻辑与状态对象,可以将原有的巨大的语句块拆开。
- 可以让多个容器(context)对象共享一个状态对象,减少对象个数。
缺点:
- 会导致设计中新增大量的类。
- 有点违反开闭原则。
- 结构与实现都比较复杂,使用不当容易导致代码混乱。