JAVA設計模式之 狀態模式【State Pattern】

小呂-ICE發表於2014-11-12

一、概述

    當系統中某個物件存在多個狀態,這些狀態之間可以進行轉換,而且物件在不同狀態下行為不相同時可以使用狀態模式。狀態模式將一個物件的狀態從該物件中分離出來,封裝到專門的狀態類中,使得物件狀態可以靈活變化。狀態模式是一種物件行為型模式。


二、適用場景

    用於解決系統中複雜物件的多種狀態轉換以及不同狀態下行為的封裝問題。簡單說就是處理物件的多種狀態及其相互轉換。


三、UML類圖

    

四、參與者

1>、AbstractState(抽象狀態類):

在抽象狀態類中定義申明瞭不同狀態下的行為抽象方法,而由子類(不同的狀態子類)中實現不同的行為操作。

2>、ConcreteState(實現具體狀態下行為的狀態子類):

抽象狀態類的子類,每一個子類實現一個與環境類(Context)的一個狀態相關的行為,每一個具體的狀態類對應環境的一種具體狀態,不同的具體狀態其行為有所不同。

3>、Context(擁有狀態物件的環境類):

擁有狀態屬性,因環境的多樣性,它可擁有不同的狀態,且在不同狀態下行為也不一樣。在環境類中維護一個抽象的狀態例項,這個例項定義當前環境的狀態(setState()方法),而將具體的狀態行為分離出來由不同的狀態子類去完成。


五、用例學習

1、抽象狀態類:State.java

/**
 * JAVA設計模式之 狀態模式
 * 抽象狀態類
 * @author  lvzb.software@qq.com
 *
 */
public abstract class State {
	/**
	 * 狀態行為抽象方法,由具體的狀態子類去實現不同的行為邏輯
	 */
	public abstract void Behavior();

}
2、具體狀態子類A:ConcreteStateA.java

/**
 * 具體的狀態子類A
 * @author  lvzb.software@qq.com
 */
public class ConcreteStateA extends State {

	@Override
	public void Behavior() {
		// 狀態A 的業務行為, 及當為該狀態下時,能幹什麼 
		// 如:手機在未欠費停機狀態下, 能正常撥打電話
		System.out.println("手機在未欠費停機狀態下, 能正常撥打電話");
	}

}
3、具體狀態子類B:ConcreteStateB.java

/**
 * 具體的狀態子類B
 * @author  lvzb.software@qq.com
 *
 */
public class ConcreteStateB extends State {

	@Override
	public void Behavior() {
		// 狀態B 的業務行為, 及當為該狀態下時,能幹什麼
		// 如:手機在欠費停機狀態下, 不 能撥打電話
		System.out.println("手機在欠費停機狀態下, 不能撥打電話");
	}

}
4、擁有狀態物件的環境類:Context.java
/**
 * 環境/上下文類<br/>
 * 擁有狀態物件,且可以完成狀態間的轉換 [狀態的改變/切換 在環境類中實現]
 * @author  lvzb.software@qq.com
 *
 */
public class Context {
	// 維護一個抽象狀態物件的引用
	private State state;
	
	/*
	 * 模擬手機的話費屬性<br/>
	 * 環境狀態如下:
	 * 1>、當  bill >= 0.00$ : 狀態正常   還能撥打電話 
	 * 2>、當  bill < 0.00$ : 手機欠費   不能撥打電話
	 */
	private double bill;
	
	/**
	 * 環境處理函式,呼叫狀態例項行為 完成業務邏輯<br/>
	 * 根據不同的狀態例項引用  在不同狀態下處理不同的行為
	 */
	public void Handle(){
		checkState();
		state.Behavior();
	}
	
	
	/**
	 * 檢查環境狀態:狀態的改變/切換 在環境類中實現
	 */
	private void checkState(){
		if(bill >= 0.00){
			setState(new ConcreteStateA());
		} else {
			setState(new ConcreteStateB());
		}
	}
	
	
	/**
	 * 設定環境狀態<br/>
	 * 私有方法,目的是 讓環境的狀態由系統環境自身來控制/切換,外部使用者無需關心環境內部的狀態
	 * @param state
	 */
	private void setState(State state){
		this.state = state;
	}


	public double getBill() {
		return bill;
	}

	public void setBill(double bill) {
		this.bill = bill;
	}
}
5、測試客戶端呼叫類:Client.java
public class Client {

	public static void main(String[] args) {
		Context context = new Context();
		context.setBill(5.50);
		System.out.println("當前話費餘額:" + context.getBill() + "$");
		context.Handle();
		
		context.setBill(-1.50);
		System.out.println("當前話費餘額:" + context.getBill() + "$");
		context.Handle();
		
		context.setBill(50.00);
		System.out.println("當前話費餘額:" + context.getBill() + "$");
		context.Handle();
	}
}
6、程式執行結果:
當前話費餘額:5.5$
手機在未欠費停機狀態下, 能正常撥打電話
當前話費餘額:-1.5$
手機在欠費停機狀態下, 不能撥打電話
當前話費餘額:50.0$
手機在未欠費停機狀態下, 能正常撥打電話

六、擴充套件

    狀態模式中 關於狀態的切換有兩種不同的實現方式

    方式一:狀態的改變/切換  在環境類中實現。  如上面的用例程式碼Context類中的checkState()方法。

/**
	 * 檢查環境狀態:狀態的改變/切換 在環境類中實現
	 */
	private void checkState(){
		if(bill >= 0.00){
			setState(new ConcreteStateA());
		} else {
			setState(new ConcreteStateB());
		}
	}

    方式二:狀態的改變/切換  在具體的狀態子類中實現。

        實現步驟如下:

        1> 在環境類Context類中 初始化一個狀態例項物件,並將環境Context物件作為子類狀態的構造引數傳遞到具體的狀態子類例項中。

       如在Context.java類中:

// 設定初始狀態
this.state = new ConcreteStateA(this);
        2> 在具體的子類狀態類中根據構造進來的context物件,通過呼叫context物件的屬性值進行業務邏輯判斷 進行狀態的檢查和切換。

        如在 具體的狀態子類ConcreteStateA.java類中:

/**
 * 具體的狀態子類A
 * @author  lvzb.software@qq.com
 */
public class ConcreteStateA extends State {
	private Context ctx;
	
    public ConcreteStateA(Context context){
    	ctx = context;
    }
	
	@Override
	public void Behavior() {
		// 狀態A 的業務行為, 及當為該狀態下時,能幹什麼 
		// 如:手機在未欠費停機狀態下, 能正常撥打電話
		System.out.println("手機在未欠費停機狀態下, 能正常撥打電話");
		checkState();
		
	}

	/**
	 * 檢查狀態 是否需要進行狀態的轉換<br/>
	 * 狀態的切換由具體狀態子類中實現
	 */
	private void checkState(){
		if (ctx.getBill() < 0.00) {
			ctx.setState(new ConcreteStateB(ctx));
		}
	}
}





相關文章