什么是状态模式
概念引入:状态模式是一种基于状态设计行为的模式。当我们需要设计这样一个系统:这个系统有许多状态,且对这个系统进行操作的有许多种方法,在系统的不同状态下,执行这些操作会导致不同的结果。这时就需要状态模式的设计理念来组织我们的代码了,以应对在多操作种类下的多状态之间的转化。
直接来分析一个例题,试着用状态模式的理念去实现。
如图:
简单分析一下:这是一个与糖果贩卖机有关的题,糖果贩卖机有四个状态:售罄、有糖果但是没有投币、有糖果且投币了、售出糖果。对应的操作有投入25分钱,返回25分钱,转动曲柄,发放糖果这个几个操作。如果直接面向过程编程或许也可以解决这个问题,但是程序中会出现大量的if,else之类的语句,程序在后期也是完全不利于维护的。我们需要采用面向接口编程的设计思想去考虑这个问题。分析程序中变化的与不变的地方,四个状态与四个操作,在每个状态下我们都可以进行这四个操作,但是反馈不同,这里可以抽象为执行相同的方法,但是方法的具体执行内容不同,对应的状态转化不同。为了便于维护与扩展,我们把状态抽象出来作为接口,四种操作也就对应了四种抽象方法,子类继承后需要去具体实现。
总结一下,如何实现:
1、写一个接口,包含四种操作(抽象方法)。
2、写四个状态的实现类,分别实现接口,重写方法,方法中包括状态的转化与具体的实现内容。
3、写一个糖果机器的类,在实例化它的时候也实例化四个状态对应的对象,也就说让它持有四个状态的引用,并为它们写get和set方法,把机器对象传入那些状态类,以便对状态进行操作。
流程的部分分析:程序可以设定初始状态,假定在没有25分钱的情况下,投入了25分钱,即机器对象调用了机器对象的投入25分钱的方法,在方法中实际上是当前state对象调用了自己的“投入25分钱”的方法,在这个方法中,令机器对象获取“有25分钱”这个状态的引用并用set方法设置当前状态为“有25分钱的状态”,系统同时输出相应的反馈。这是一个小步骤,但通过这个步骤也足以窥见整体的设计思路了。
这样设计的好处:以后如果要增加或者修改状态,只需要对这些被抽离出来的类单独进行调整就可以了,而不用修改客户代码,客户代码也仅仅持有状态的引用,而不知道具体的内容,这就实现了客户代码和具体行为的解耦。
和策略模式的区别:两者都把需要改变的行为抽象出来了,且都是动态改变。区别是状态模式对于状态之间的转化关系都是严密设计好的,类似于上述图示,过程都有严密的逻辑关系,而策略模式没有体现这个特点。
Java实现糖果售货机
一、状态接口:
public interface State {
public abstract void giveOutCandies();
public abstract void addMoney();
public abstract void giveBackMoney();
public abstract void turnHandle();
}
1、糖果卖完的状态类:
public class SoldOut implements State{
private Machine machine;
public SoldOut(Machine machine) {
this.machine=machine;
}
public void giveOutCandies() {
System.out.println("没有糖果,不能分发");
}
public void addMoney() {
System.out.println("没有糖果,放入钱也没有用");
}
public void giveBackMoney() {
System.out.println("没有糖果,取不出钱");
}
public void turnHandle() {
System.out.println("没有糖果,转动手柄,没有现象");
}
}
** 2、机器没有得到钱的状态(还没有用户塞钱进去)**:
public class NoMoney implements State{
private Machine machine;
public NoMoney(Machine machine) {
this.machine=machine;
}
public void giveOutCandies() {
System.out.println("当前没有糖果可以取出");
}
public void addMoney() {
System.out.println("加钱");
System.out.println("进入有钱的状态");
machine.setState(machine.getHaveMoney());
}
public void giveBackMoney() {
System.out.println("没有钱可以退");
}
public void turnHandle() {
System.out.println("转动手柄,没有反应");
}
}
** 3、机器获得钱的状态(用户塞钱进去了)**:
public class HaveMoney implements State{
private Machine machine;
public HaveMoney(Machine machine) {
this.machine=machine;
}
public void giveOutCandies() {
System.out.println("当前糖果还不能取出来");
}
public void addMoney() {
System.out.println("已经收到钱了,不需要再加钱");
}
public void giveBackMoney() {
System.out.println("退钱成功");
System.out.println("回到等待加钱的状态");
machine.setState(machine.getNoMoney());
}
public void turnHandle() {
System.out.println("转动手柄,请准备取糖果");
machine.setState(machine.getSoldCandy());;
}
}
** 4、机器准备出糖果的状态**:
public class SoldCandy implements State{
private Machine machine;
public SoldCandy(Machine machine) {
this.machine=machine;
}
public void giveOutCandies() {
System.out.println("从机器获取糖果成功");
machine.setCount(machine.getCount()-1);
if(machine.getCount()==0){
System.out.println("没糖果了");
machine.setState(machine.getSoldOut());
}else{
System.out.println("还有糖果继续卖");
machine.setState(machine.getNoMoney());
}
}
public void addMoney() {
System.out.println("不能加钱,还有糖果没有拿出来呢");
}
public void giveBackMoney() {
System.out.println("不能退钱,还有糖果没有拿出来呢");
}
public void turnHandle() {
System.out.println("转动手柄无效,有糖果未取出");
}
}
** 二、卖糖果机器的类**:
public class Machine {
//四个状态的引用
private SoldCandy soldCandy;
private SoldOut soldOut;
private HaveMoney haveMoney;
private NoMoney noMoney;
private State state;//当前状态
private int count;//糖果数
/**
* 构造方法
* @param count
*/
public Machine(int count) {
this.count=count;
soldCandy = new SoldCandy(this);
soldOut=new SoldOut(this);
haveMoney=new HaveMoney(this);
noMoney=new NoMoney(this);
state=noMoney;
}
/**
* 获取当前糖果数量
* @return
*/
public int getCount() {
return this.count;
}
/**
* 设置糖果数
* @param count
*/
public void setCount(int count){
this.count=count;
}
/**
* 出糖果
*/
public void giveOutCandies(){
state.giveOutCandies();
}
/**
* 放入钱
*/
public void addMoney(){
state.addMoney();
}
/**
* 退出钱
*/
public void giveBackMoney(){
state.giveBackMoney();
}
/**
* 转动手柄
*/
public void turnHandle(){
state.turnHandle();
}
public SoldCandy getSoldCandy() {
return soldCandy;
}
public SoldOut getSoldOut() {
return soldOut;
}
public HaveMoney getHaveMoney() {
return haveMoney;
}
public NoMoney getNoMoney() {
return noMoney;
}
/**
* 设置状态
* @param state
*/
public void setState(State state){
this.state=state;
}
}
** 三、测试类**:
public class Test {
public static void main(String[] args){
Machine mc=new Machine(5);
mc.addMoney();
mc.turnHandle();
mc.giveOutCandies();
System.out.println("+++++++++++++++++++++++");
mc.addMoney();
mc.giveBackMoney();
mc.addMoney();
mc.turnHandle();
mc.giveOutCandies();
System.out.println("+++++++++++++++++++++++");
mc.addMoney();
mc.turnHandle();
mc.giveOutCandies();
mc.addMoney();
mc.turnHandle();
mc.giveOutCandies();
mc.addMoney();
mc.turnHandle();
mc.giveOutCandies();
mc.addMoney();
mc.turnHandle();
mc.giveOutCandies();
}
}
** 四、运行结果**:
简单分析一下测试的过程:先塞钱给机器,然后转动手柄,再取糖果;然后测试了退钱的操作;最后一口气把糖果取完,测试当糖果没有后的状态输出。结果和设计的以及预想的一致,实现了通过机器的状态转化功能。
小结:面对有多个状态的系统,同时也有多种操作,就可以用状态模式来设计这个系统,这可以充分考虑到每一个变化的状态,也能为后期的状态修改创造条件,同时客户代码仅仅持有状态的引用,而不关心其具体内容,这就实现了具体状态和状态调用的解耦。。