设计模式之状态模式
1. 什么是状态模式
State模式也叫状态模式,是行为设计模式的一种。State模式允许通过改变对象的内部状态而改变对象的行为,这个对象表现得就好像修改了它的类一样。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转译到表现不同状态的一系列类当中,可以把复杂的判断逻辑简化。
-
Context:用户对象拥有一个State类型的成员,以标识对象的当前状态;
-
State:接口或基类封装与Context的特定状态相关的行为;
-
ConcreteState:接口实现类或子类实现了一个与Context某个状态相关的行为。
2. 具体实例
智能糖果机,用Java软件控制糖果机,他有以下几个动作:
待机、投入一元硬币、转动把手、滑落一颗糖果、待机(根据机器内糖果库存情况,是否提示售罄)。
传统的设计怎么设计呢?很容易想到,我们就把糖果机封装成一个类,然后里面有具体的状态函数,在不同的状态函数中去判断和更新状态。具体代码如下:
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;
}
}
}
这种设计没有什么问题吧,可以实现现有的需要。但是现在有了新的需求了,糖果机有新的功能要添加,比如有一个幸运功能,当转到幸运功能时会给出更多的糖果,对于这个功能我们可以在上面的类中去添加,然后更改每一个方法,这样做的话其实不符合开闭原则了。我们的上面设计其实是面向功能来做的,而我们应该做的是面向接口去编程,实现对修改封闭,对扩展开放。
根据上面的想法,我们想这个项目中什么是改变的,什么是不变的呢?其实动作是不变的,就那几个动作,然后状态是改变的,我们就把改变的地方抽取出来然后设计成接口,由具体的状态去扩展继承。
类图如下:
我们想状态模式能根据内部状态的变化,改变对象的行为,看起来好像修改了类。上面的设计就是根据这种模式设计的。我们把改变的状态抽象出来,然后取让行为跟着状态变化。
具体的代码实现:
状态接口:
public interface State {
public void insertCoin();
public void returnCoin();
public void turnCrank();
public void dispense();
public void printstate();
}
具体的状态:
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();
}
}
在糖果机这个类中我们只需要初始化所有的State,然后在具体的行为中去使用state下的行为。其实也就是内部状态的变化改变了对象的行为。
测试类:
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();
}
}