原理或定義
當一個對象的內在狀態改變時允許改變其行爲,這個對象看起來像是改變了其類。狀態模式中的行爲是由狀態來決定的,不同的狀態對應了不同的行爲。
結構
環境類Context:
它定義了客戶程序需要的接口並維護一個具體狀態角色的實例,將與狀態相關的操作委託給當前的Concrete State對象來處理;
抽象狀態類State:
定義一個接口以封裝與Context的一個特定狀態相關的行爲;
具體狀態類ConcreteState:
每一子類實現一個與Context的一個狀態相關的行爲;
類圖
案例與代碼
本模式使用一個糖果機項目來做示例
智能糖果機,用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 class MainTest {
public static void main(String[] args) {
CandyMachine mCandyMachine=new CandyMachine(1);
mCandyMachine.printstate();
mCandyMachine.insertCoin();
mCandyMachine.printstate();
mCandyMachine.turnCrank();
mCandyMachine.printstate();
mCandyMachine.insertCoin();
mCandyMachine.printstate();
mCandyMachine.turnCrank();
mCandyMachine.printstate();
}
}
加入遊戲元素:有10%的概率可以拿到2粒糖果。
以上的設計會違反開閉原則,並且對狀態的維護麻煩。
開閉原則:對擴展開放,對修改原有的代碼關閉。
狀態模式的設計方案:
類圖:
狀態接口:
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 SoldOutState implements State {
private CandyMachine mCandyMachine;
public SoldOutState(CandyMachine mCandyMachine)
{
this.mCandyMachine=mCandyMachine;
}
@Override
public void insertCoin() {
// TODO Auto-generated method stub
System.out.println("you can't insert coin,the machine sold out!");
}
@Override
public void returnCoin() {
// TODO Auto-generated method stub
System.out
.println("you can't return,you haven't inserted a coin yet!");
}
@Override
public void turnCrank() {
// TODO Auto-generated method stub
System.out.println("you turned,but there are no candies!");
}
@Override
public void dispense() {
// TODO Auto-generated method stub
}
@Override
public void printstate() {
// TODO Auto-generated method stub
System.out.println("***SoldOutState***");
}
}
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();
}
}
狀態模式:能根據內部狀態的變化,改變對象的行爲,看起來好像修改了類。不同的狀態對應不同的類文件,增加了系統文件個數。
使用場景
1. 一個對象的行爲取決於他的狀態,並且它必須在運行時根據狀態改變它的行爲;
2.一個操作中含有龐大的多分支結構,並且這些分支決定於對象的狀態
優缺點
主要優點有:
1.狀態模式將不同狀態所對應的行爲彼此分隔開來,降低程序的耦合,從而在新增或修改狀態時,可以避免程序互相影響。
2.狀態模式將狀態的邏輯處理變化交由上下文對象Context管理,便於客戶端的調用
缺點主要有:
不同的狀態對應不同的類文件,增加了系統文件個數,不便於維護管理。