設計模式學習筆記之狀態模式

birdlove1987發表於2017-03-19

什麼是狀態模式呢?

允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它的類。。。是不是聽起來顯示在內部進行零件調換的感覺,其實是有點類似這種想法的,只不過調換的零件就是封裝好的狀態。

讓我們來舉個例子




在我還上學的時候,寢室樓下有一臺這樣的自動販售機,專門賣飲料。每天晚上寢室關門之後口渴了,在這裡面買點飲料喝喝還是很方便的。

下面用程式來模擬一下這個售貨機:


可樂售貨機類:


/**
 * 
 * 可樂自動販售機類
 * @author birdlove1987
 *
 */
public class VendingMachine 
{
	
	//售空
	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 VendingMachine(int count) 
	{
		this.count = count;
		if (count > 0) {
			state = OnReadyState;
		}
	}

	//向販售機投幣函式
	public void insertCoin()
	{
		switch (state)
		{
			case SoldOutState:
				System.out.println("請不要投幣了,售貨機裡已經沒有可樂了!");
				break;
			case OnReadyState:
				state = HasCoin;
				System.out.println("投幣成功,請按出貨按鈕!");
				break;
			case HasCoin:
				System.out.println("售貨機裡以及有硬幣了,請勿重複投幣!");
				break;
			case SoldState:
				System.out.println("正在出貨請勿投幣!");
				break;
		}
	}

	//販售機退幣函式
	public void returnCoin() 
	{
		switch (state)
		{
			case SoldOutState:
				System.out.println("您還沒有投幣!");
				break;
			case OnReadyState:
				System.out.println("您還沒有投幣!");
				break;
			case HasCoin:
				System.out.println("請在退幣口收好您的硬幣");
				state = OnReadyState;
				break;
			case SoldState:
				System.out.println("可樂已售出,不能退幣");
				break;
		}
	}

	//按出可樂按鈕函式
	public void pressButton() 
	{
		switch (state) 
		{
			case SoldOutState:
				System.out.println("對不起,可樂已售空!");
				break;
			case OnReadyState:
				System.out.println("請您先投幣!");
				break;
			case HasCoin:
				System.out.println("請稍等,出可樂中!");
				state = SoldState;
				appearCola();
				break;
			case SoldState:
				System.out.println("可樂已出,如想再次購買請再次投幣!");
			break;
		}
	}

	//售貨機出可樂函式
	private void appearCola() 
	{
		count = count - 1;
		System.out.println("出貨口,滾出了一瓶可樂!");
		if (count > 0)
		{
			state = OnReadyState;
		} 
		else
		{
			System.out.println("對不起,可樂已經售空了,請勿在投幣!");
			state = SoldOutState;
		}
	}

	//列印目前售貨機的狀態
	public void printState() 
	{
		switch (state) 
		{
			case SoldOutState:
				System.out.println("=========售空=========");
				break;
			case OnReadyState:
				System.out.println("=========售賣中=========");
				break;
			case HasCoin:
				System.out.println("=========已投幣=========");
				break;
			case SoldState:
				System.out.println("=========出貨中=========");
				break;
		}
	}
}


寫個測試類來測試一下

測試類


/**
 * 測試類
 * @author birdlove1987
 *
 */
public class MainTest 
{
	public static void main(String[] args) 
	{
		//建立售貨機例項,並且只放入一瓶可樂
		VendingMachine mVendingMachine=new VendingMachine(1);
		
		
		//列印售貨機狀態
		mVendingMachine.printState();
		
		//投幣
		mVendingMachine.insertCoin();
		
		//列印售貨機狀態
		mVendingMachine.printState();
		
		//按出貨按鈕
		mVendingMachine.pressButton();
		
		
		//列印售貨機狀態
		mVendingMachine.printState();
		
		//投幣
		mVendingMachine.insertCoin();
		
		//列印售貨機狀態
		mVendingMachine.printState();
		
		//按出貨按鈕
		mVendingMachine.pressButton();
		
		//列印售貨機狀態
		mVendingMachine.printState();
	}
}



嗯!模擬成功了!好像感覺還不錯呢!不過買了一段時間,你可能發現,如果不搞一些促銷活動,更本就搞不過那些有促銷的商家!




所以自動售貨機也要搞一個“再來一瓶”的活動啊!

但是!!!如果現在直接在自動售貨機的類上去直接修改,就違背了軟體設計裡的“開-閉原則”了,破壞了我們已經閉合的類。所以現在看來,這種設計並不好。。

下面就輪到狀態設計模式登場啦,我們從新構建類,這次以狀態為類,來構建。

首先定義一下狀態介面


/***
 * 狀態介面
 * @author birdlove1987
 *
 */
public interface State 
{
	
	//投幣函式
	public void insertCoin();
	
	//退幣函式
	public void returnCoin();
	
	//按出可樂按鈕函式
	public void pressButton();
	
	//出可樂函式
	public void appearCola();
	
	//列印狀態函式
	public void printState();
	
}


再來定義狀態類


/***
 * 在售狀態類
 * @author birdlove1987
 *
 */
public class OnReadyState implements State 
{
	
	//販售機引用
	private VendingMachine mVendingMachine;
	
	//建構函式初始化
	public OnReadyState(VendingMachine mVendingMachine)
	{
		this.mVendingMachine=mVendingMachine;
	}

	
	@Override
	public void insertCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("投幣成功,請按出貨按鈕!");
		mVendingMachine.setState(mVendingMachine.mHasCoin);
	}

	@Override
	public void returnCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("您還沒有投幣!");
		
	}

	@Override
	public void pressButton() 
	{
		// TODO Auto-generated method stub
		System.out.println("請您先投幣!");
		
	}

	@Override
	public void appearCola() 
	{
		// TODO Auto-generated method stub
	}

	@Override
	public void printState() 
	{
		// TODO Auto-generated method stub
		System.out.println("=========售賣中=========");
	}
}


/***
 * 售空狀態類
 * @author birdlove1987
 *
 */
public class SoldOutState implements State 
{

	private VendingMachine mVendingMachine;
	public SoldOutState(VendingMachine mCandyMachine)
	{
		this.mVendingMachine=mVendingMachine;
	}

	@Override
	public void insertCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("請不要投幣了,售貨機裡已經沒有可樂了!");
		
	}

	@Override
	public void returnCoin() 
	{
		// TODO Auto-generated method stub
		System.out
		.println("您還沒有投幣!");

	}

	@Override
	public void pressButton() 
	{
		// TODO Auto-generated method stub
		System.out.println("對不起,可樂已售空!");
		
	}

	@Override
	public void appearCola() 
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void printState() {
		// TODO Auto-generated method stub
		System.out.println("=========售空=========");
	
	}

}


/***
 * 已投幣狀態類
 * @author birdlove1987
 *
 */
public class HasCoin implements State 
{	
	private VendingMachine mVendingMachine;
	public HasCoin(VendingMachine mVendingMachine) 
	{
		this.mVendingMachine = mVendingMachine;
	}

	@Override
	public void insertCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("售貨機裡以及有硬幣了,請勿重複投幣!");

	}

	@Override
	public void returnCoin()
	{
		// TODO Auto-generated method stub
		System.out.println("請在退幣口收好您的硬幣");
		mVendingMachine.setState(mVendingMachine.mOnReadyState);
	}

	@Override
	public void pressButton()
	{
		// TODO Auto-generated method stub
		System.out.println("請稍等,出可樂中!");
		
		//建立隨機中獎概率
		Random anotherOne = new Random();
		
		//20%的機會再來一瓶
		int luck = anotherOne.nextInt(5);
		if(luck==0)
		{
			mVendingMachine.setState(mVendingMachine.mOneMoreCola);
		}else
		{
			mVendingMachine.setState(mVendingMachine.mSoldState);
		}
	}

	@Override
	public void appearCola()
	{
	}

	@Override
	public void printState() 
	{
		// TODO Auto-generated method stub
		System.out.println("=========已投幣=========");

	}
}


/**
 * 出可樂狀態類
 * @author birdlove1987
 *
 */
public class SoldState implements State 
{
	private VendingMachine mVendingMachine;
	public SoldState(VendingMachine mVendingMachine)
	{
		this.mVendingMachine=mVendingMachine;
	}

	@Override
	public void insertCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("正在出貨請勿投幣!");
	}

	@Override
	public void returnCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("可樂已售出,不能退幣");
		
	}

	@Override
	public void pressButton()
	{
		// TODO Auto-generated method stub
		System.out
		.println("可樂已出,如想再次購買請再次投幣!");

	}

	@Override
	public void appearCola() 
	{
		// TODO Auto-generated method stub
		
		mVendingMachine.appearCola();
		if (mVendingMachine.getCount() > 0) 
		{
			mVendingMachine.setState(mVendingMachine.mOnReadyState);
		}
		else 
		{
			System.out.println("對不起,可樂已經售空了,請勿在投幣!");
			mVendingMachine.setState(mVendingMachine.mSoldOutState);
		}
	}
	
	@Override
	public void printState()
	{
		// TODO Auto-generated method stub
		System.out.println("=========出貨中=========");	
	}
}



/***
 * 再來一瓶類
 * @author birdlove1987
 *
 */
public class OneMoreCola implements State 
{

	private VendingMachine mVendingMachine;

	public OneMoreCola(VendingMachine mVendingMachine) 
	{
		this.mVendingMachine = mVendingMachine;
	}

	@Override
	public void insertCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("正在出貨請勿投幣!");
	}

	@Override
	public void returnCoin() 
	{
		// TODO Auto-generated method stub
		System.out.println("可樂已售出,不能退幣");

	}

	@Override
	public void pressButton() 
	{
		// TODO Auto-generated method stub
		System.out.println("請稍等,出可樂中!");

	}

	@Override
	public void appearCola() 
	{
		// TODO Auto-generated method stub

		
		mVendingMachine.appearCola();
		if (mVendingMachine.getCount() == 0) 
		{
			mVendingMachine.setState(mVendingMachine.mSoldOutState);
		} 
		else 
		{
			System.out.println("恭喜你獲得了再來一瓶,馬上為您再出一瓶可樂");
			mVendingMachine.appearCola();
			if (mVendingMachine.getCount() > 0) 
			{
				mVendingMachine.setState(mVendingMachine.mOnReadyState);
			}
			else 
			{
				System.out.println("對不起,可樂已經售空了,請勿在投幣!");
				mVendingMachine.setState(mVendingMachine.mSoldOutState);
			}
		}

	}

	@Override
	public void printState() 
	{
		// TODO Auto-generated method stub
		System.out.println("=========再來一瓶=========");
	}
}






構建自動販售機類


/**
 * 自動售貨機類
 * @author birdlove1987
 *
 */
public class VendingMachine 
{
	//狀態類引用
	State mSoldOutState;
	State mOnReadyState;
	State mHasCoin;
	State mSoldState;
	State mOneMoreCola;
	
	//目前狀態
	private State state;
	
	//可樂計數器
	private int count = 0;

	//建構函式
	public VendingMachine(int count) 
	{
		this.count = count;
		mSoldOutState = new SoldOutState(this);
		mOnReadyState = new OnReadyState(this);
		mHasCoin = new HasCoin(this);
		mSoldState = new SoldState(this);
		mOneMoreCola = new OneMoreCola(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 pressButton() 
	{
		state.pressButton();
		state.appearCola();
	}

	//出可樂函式
	void appearCola() 
	{
		// TODO Auto-generated method stub
		if (count > 0)
		{
			count = count - 1;
			System.out.println("出貨口,滾出了一瓶可樂!");
		}

	}

	//可樂計數函式
	public int getCount() 
	{
		return count;
	}
	
	//列印狀態函式
	public void printState() 
	{
		state.printState();
	}
}


最後寫一個測試類測試一下


/**
 * 測試類
 * @author birdlove1987
 *
 */
public class MainTest 
{
	public static void main(String[] args) 
	{
		
		//例項化自動販售機物件
		VendingMachine mVendingMachine = new VendingMachine(10);
		
		//列印狀態
		mVendingMachine.printState();

		//投幣
		mVendingMachine.insertCoin();
		
		//列印狀態
		mVendingMachine.printState();

		//按出可樂按鈕
		mVendingMachine.pressButton();

		//列印狀態
		mVendingMachine.printState();

		//投幣
		mVendingMachine.insertCoin();
		
		//列印狀態
		mVendingMachine.printState();

		//按出可樂按鈕
		mVendingMachine.pressButton();

		//列印狀態
		mVendingMachine.printState();
	}
}




臥槽!一次都沒中獎,什麼鬼!!!不行,買買買!!!




中了一次。。。好吧,我來作弊一下。。調到100%




哈哈哈!


狀態模式的優點
    1.狀態模式將與特定狀態相關的行為區域性化,並且將不同狀態的行為分割開來。
    2.將與狀態有關的行為封裝到一個類中,方便地增加新的狀態,只需改變物件狀態即可改變物件的行為。 
    3.提供程式碼複用性,降低了程式的某些耦合性。   
     
狀態模式的缺點
    1.有時候狀態類會很多,而且邏輯複雜。

狀態模式適用場景
    1、物件的行為依賴於它的狀態,並且其行為依據它的狀態改變而改變。 
    2、程式碼中包含大量與物件狀態有關的條件語句







相關文章