今天我们来探讨一下设计模式中的状态模式。
假设我们需要完成这样一个需求,设计一个糖果机程序:
顾客需要往糖果机投入硬币,然后摇动糖果机把手,糖果机再给顾客吐糖果。
这里顾客有以下动作:
1)投硬币
2)取消,吐回硬币
2)转动把手
3)拿到糖果
糖果机也有几种状态:
1)待机
2)收到硬币
3)吐完糖果
4)售罄
顾客所做的动作,根据糖果机状态的不同而有不同的结果。
状态图如下:
传统的做法是,顾客的每个行为对应一个函数,在函数中使用switch根据不同的状态执行不同的代码。如下。
public class CandyMachine { final static int SoldOutState = 0;//卖完 final static int OnReadyState = 1;//待机 final static int HasCoin = 2;//有硬币 final static int SoldState = 3;//吐完糖果 private int state = SoldOutState; private int count = 0; public CandyMachine(int count) { this.count = count; if (count > 0) { state = OnReadyState; } } public void insertCoin() { switch (state) { case SoldOutState: System.out.println("you can‘t insert coin,the machine sold out!"); break; case OnReadyState: state = HasCoin; System.out .println("you have inserted a coin,next,please turn crank!"); break; case HasCoin: System.out.println("you can‘t insert another coin!"); break; case SoldState: System.out.println("please wait!we are giving you a candy!"); break; } } public void returnCoin() { switch (state) { case SoldOutState: System.out .println("you can‘t return,you haven‘t inserted a coin yet!"); break; case OnReadyState: System.out.println("you haven‘t inserted a coin yet!"); break; case HasCoin: System.out.println("coin return!"); state = OnReadyState; break; case SoldState: System.out.println("sorry,you already have turned the crank!"); break; } } public void turnCrank() { switch (state) { case SoldOutState: System.out.println("you turned,but there are no candies!"); break; case OnReadyState: System.out.println("you turned,but you haven‘t inserted a coin!"); break; case HasCoin: System.out.println("crank turn...!"); state = SoldState; dispense(); break; case SoldState: System.out .println("we are giving you a candy,turning another get nothing,!"); break; } } private void dispense() { count = count - 1; System.out.println("a candy rolling out!"); if (count > 0) { state = OnReadyState; } else { System.out.println("Oo,out of candies"); state = SoldOutState; } } public void printstate() { switch (state) { case SoldOutState: System.out.println("***SoldOutState***"); break; case OnReadyState: System.out.println("***OnReadyState***"); break; case HasCoin: System.out.println("***HasCoin***"); break; case SoldState: System.out.println("***SoldState***"); break; } } }
这样完全满足需求中的描述。
新需求:加入游戏元素,每次摇动把手都有10%的概率拿到2颗糖果。
点评:传统的做法显然是不符合开闭原则的,如果有新需求将需要修改已有的代码,为了满足新需求,需要在每个动作中加入新的switch分支,因此破坏了对修改关闭的原则。这不是好的设计。好的设计应该是针对接口的开发,而不是功能的开发。在做设计的时候需要思考那些是改变的,那些是不变的。
通过分析,我们发现动作是不变的,而状态可能会增加。因此,我们可以通过在接口中定义不变的动作,新的状态只需要增加相应的状态实现类即可。
下面,介绍状态模式如何解决以上的问题。
状态模式的定义:能根据内部状态的变化,改变对象的行为,看起来好像修改了类。
通过状态模式重构的类图如下。
原来的状态由整数变成对象。代码如下。
public interface State { public void insertCoin(); public void returnCoin(); public void turnCrank(); public void dispense(); public void printstate(); }
public class OnReadyState implements State { private CandyMachine mCandyMachine; public OnReadyState(CandyMachine mCandyMachine) { this.mCandyMachine=mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out .println("you have inserted a coin,next,please turn crank!"); mCandyMachine.setState(mCandyMachine.mHasCoin); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven‘t inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out.println("you turned,but you haven‘t inserted a coin!"); } @Override public void dispense() { // TODO Auto-generated method stub } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***OnReadyState***"); } }
public class OnReadyState implements State { private CandyMachine mCandyMachine; public OnReadyState(CandyMachine mCandyMachine) { this.mCandyMachine=mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out .println("you have inserted a coin,next,please turn crank!"); mCandyMachine.setState(mCandyMachine.mHasCoin); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven‘t inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out.println("you turned,but you haven‘t inserted a coin!"); } @Override public void dispense() { // TODO Auto-generated method stub } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***OnReadyState***"); } }
public class SoldState implements State { private CandyMachine mCandyMachine; public SoldState(CandyMachine mCandyMachine) { this.mCandyMachine=mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("please wait!we are giving you a candy!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven‘t inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out .println("we are giving you a candy,turning another get nothing,!"); } @Override public void dispense() { // TODO Auto-generated method stub mCandyMachine.releaseCandy(); if (mCandyMachine.getCount() > 0) { mCandyMachine.setState(mCandyMachine.mOnReadyState); } else { System.out.println("Oo,out of candies"); mCandyMachine.setState(mCandyMachine.mSoldOutState); } } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***SoldState***"); } }
public class WinnerState implements State { private CandyMachine mCandyMachine; public WinnerState(CandyMachine mCandyMachine) { this.mCandyMachine = mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("please wait!we are giving you a candy!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven‘t inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out .println("we are giving you a candy,turning another get nothing,!"); } @Override public void dispense() { // TODO Auto-generated method stub mCandyMachine.releaseCandy(); if (mCandyMachine.getCount() == 0) { mCandyMachine.setState(mCandyMachine.mSoldOutState); } else { System.out.println("you are a winner!you get another candy!"); mCandyMachine.releaseCandy(); if (mCandyMachine.getCount() > 0) { mCandyMachine.setState(mCandyMachine.mOnReadyState); } else { System.out.println("Oo,out of candies"); mCandyMachine.setState(mCandyMachine.mSoldOutState); } } } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***WinnerState***"); } }
public class HasCoin implements State { private CandyMachine mCandyMachine; public HasCoin(CandyMachine mCandyMachine) { this.mCandyMachine = mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("you can‘t insert another coin!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("coin return!"); mCandyMachine.setState(mCandyMachine.mOnReadyState); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out.println("crank turn...!"); Random ranwinner=new Random(); int winner=ranwinner.nextInt(10); if(winner==0) { mCandyMachine.setState(mCandyMachine.mWinnerState); }else { mCandyMachine.setState(mCandyMachine.mSoldState); } } @Override public void dispense() { } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***HasCoin***"); } }
public class CandyMachine { State mSoldOutState; State mOnReadyState; State mHasCoin; State mSoldState; State mWinnerState; private State state; private int count = 0; public CandyMachine(int count) { this.count = count; mSoldOutState = new SoldOutState(this); mOnReadyState = new OnReadyState(this); mHasCoin = new HasCoin(this); mSoldState = new SoldState(this); mWinnerState = new WinnerState(this); if (count > 0) { state = mOnReadyState; } else { state = mSoldOutState; } } public void setState(State state) { this.state = state; } public void insertCoin() { state.insertCoin(); } public void returnCoin() { state.returnCoin(); } public void turnCrank() { state.turnCrank(); state.dispense(); } void releaseCandy() { // TODO Auto-generated method stub if (count > 0) { count = count - 1; System.out.println("a candy rolling out!"); } } public int getCount() { return count; } public void printstate() { state.printstate(); } }
测试类
public class MainTest { public static void main(String[] args) { CandyMachine mCandyMachine = new CandyMachine(6); mCandyMachine.printstate(); mCandyMachine.insertCoin(); mCandyMachine.printstate(); mCandyMachine.turnCrank(); mCandyMachine.printstate(); mCandyMachine.insertCoin(); mCandyMachine.printstate(); mCandyMachine.turnCrank(); mCandyMachine.printstate(); } }
点评:根据顾客动作,在不同状态间切换。需要有新的状态时候,只需要增加类即可,因为动作是不变的。
原文:http://my.oschina.net/gaohongtian/blog/491843