天天看點

JAVA設計模式--狀态模式

狀态模式

上文我們講到了政策模式,本文呢,我們來一起認識一下政策模式的雙胞胎兄弟:狀态模式。

狀态模式(State Pattern),當一個對象的内在狀态改變時允許改變其行為,這個對象看起來就像改變了其類。

我第一遍讀到這個定義的時候,簡直是一臉懵逼狀态,不知道他在說什麼,一句一句研究吧,當一個對象的内在狀态改變時允許改變其行為,因為這個模式将狀态封裝為獨立

的類,并将動作委托到代表目前狀态的類,是以,對象的行為會随着内在狀态的改變而改變;這個對象看起來就像改變了其類,從客戶的視角來看:如果說你使用的對象能夠

完全改變它的行為,那麼你會覺得,這個對象實際上是從别的類執行個體化而來的。然而,實際上,你知道我們是在使用組合通過簡單引用不同的狀态對象來造成類改變的假象。

來看下狀态模式的類圖吧:

JAVA設計模式--狀态模式

一看,跟政策模式的類圖結構完全沒差啊,但是兩者在意圖上是有着差别的,我們來看一個狀态模式的例子,然後再去說明狀态模式與政策模式有哪些差別吧。

任務管理軟體大家都了解吧,任務有未啟動、進行中、已解決、已關閉等狀态,我們可以對任務進行啟動任務、錄入工作日志、提出解決方案、關閉任務等操作,假如,我們對

任務進行啟動操作,那麼任務處于不同狀态下就會産生不同的反應。

這個操作的代碼流程怎麼實作?首先,我是這樣實作的:

package com.hirain.chc.state;

public class Task {
	
	//定義任務的幾種狀态
	public static final int NOT_START = 0; //未啟動
	public static final int UNDERWAY = 1;  //運作中
	public static final int RESOLVED = 2;  //已解決
	public static final int CLOSED = 3;    //已關閉
	
	//指定目前狀态
	public int state = NOT_START;
	
	/**
	 * 啟動操作.
	 */
	public void startUp(){
		if (state == 0) {
			System.out.println("啟動成功,任務切換到運作中狀态了");
			state = 1;
		} else if (state == 1) {
			System.out.println("啟動失敗,目前任務正處于運作中狀态");
		} else if (state == 2) {
			System.out.println("啟動失敗,目前任務正處于已解決狀态");
		} else if (state == 3) {
			System.out.println("啟動失敗,目前任務正處于已關閉狀态");
		}
	}
	
	/**
	 * 錄入工作内容操作.
	 */
	public void writeContent() {
		if (state == 0) {
			System.out.println("寫入失敗,任務尚未啟動");
			state = 1;
		} else if (state == 1) {
			System.out.println("寫入成功");
		} else if (state == 2) {
			System.out.println("寫入失敗,任務已經處于已解決狀态");
		} else if (state == 3) {
			System.out.println("寫入失敗,任務已經處于已關閉狀态");
		}
	}
	
	/**
	 * 提出解決方案操作.
	 */
	public void solve () {
		if (st<span style="white-space:pre">	</span>ate == 0) {
			System.out.println("操作失敗,任務尚未啟動");
			state = 1;
		} else if (state == 1) {
			System.out.println("操作成功,任務已更新至已解決狀态");
			state = 2;
		} else if (state == 2) {
			System.out.println("操作失敗,任務已經處于已解決狀态");
		} else if (state == 3) {
			System.out.println("操作失敗,任務已經處于已關閉狀态");
		}
	}
	
	/**
	 * 關閉任務操作.
	 */
	public void close() {
		if (state == 0) {
			System.out.println("操作失敗,任務尚未啟動");
			state = 1;
		} else if (state == 1) {
			System.out.println("操作成功,任務正處于運作中狀态,不可關閉");
			state = 2;
		} else if (state == 2) {
			System.out.println("操作成功,任務已經更新至已關閉狀态");
			state = 3;
		} else if (state == 3) {
			System.out.println("操作失敗,任務已經處于已關閉狀态");
		}
	}
}
           

嗯,初步看來這個類滿足了我們的需求,但是,如果我們某一天要新增一種狀态呢,該怎麼修改代碼以适應我們的需求?

挂起狀态:運作中的任務可切換至挂起狀态,表示任務需要暫時暫停。

如果基于上面那個代碼來更改的話,那麼我們需要增加一個變量,然後再每個方法中再增加一種判斷條件,代碼修改量太大,容易出現bug引起整個系統的不穩定。

是以,這種實作方式是不可行的,我們用狀态模式來解決一下這個問題,按照狀态模式的類圖來設計一下這個代碼:

package com.hirain.chc.state.demo;

import com.hirain.chc.state.demo.state.ClosedState;
import com.hirain.chc.state.demo.state.ITaskState;
import com.hirain.chc.state.demo.state.NotStartState;
import com.hirain.chc.state.demo.state.ResolvedState;
import com.hirain.chc.state.demo.state.UnderwayState;

public class Task {
	
	public ITaskState notStartState;
	public ITaskState underwayState;
	public ITaskState resolvedState;
	public ITaskState closedState;
	
	/**
	 * 目前狀态.
	 */
	public ITaskState state;
	
	/**
	 * 構造方法内初始化所有的狀态.
	 */
	public Task() {
		notStartState = new NotStartState(this);
		underwayState = new UnderwayState(this);
		resolvedState = new ResolvedState(this);
		closedState = new ClosedState(this);
		state = notStartState;
	}
	
	/**
	 * 啟動操作.
	 */
	public void startUp() {
		state.startUp();
	}
	
	/**
	 * 錄入工作内容.
	 */
	public void writeContent() {
		state.writeContent();
	}
	
	/**
	 * 提出解決方案操作.
	 */
	public void solve () {
		state.solve();
	}
	
	/**
	 * 關閉任務操作.
	 */
	public void close() {
		state.close();
	}

	public ITaskState getState() {
		return state;
	}

	public void setState(ITaskState state) {
		this.state = state;
	}

	public ITaskState getNotStartState() {
		return notStartState;
	}

	public void setNotStartState(ITaskState notStartState) {
		this.notStartState = notStartState;
	}

	public ITaskState getUnderwayState() {
		return underwayState;
	}

	public void setUnderwayState(ITaskState underwayState) {
		this.underwayState = underwayState;
	}

	public ITaskState getResolvedState() {
		return resolvedState;
	}

	public void setResolvedState(ITaskState resolvedState) {
		this.resolvedState = resolvedState;
	}

	public ITaskState getClosedState() {
		return closedState;
	}

	public void setClosedState(ITaskState closedState) {
		this.closedState = closedState;
	}
	
}
           
package com.hirain.chc.state.demo.state;

public interface ITaskState {
	
	public void startUp();
	
	public void writeContent();
	
	public void solve();
	
	public void close ();
	
}
           
package com.hirain.chc.state.demo.state;

import com.hirain.chc.state.demo.Task;

public class NotStartState implements ITaskState {
	
	Task task;
	
	public NotStartState(Task task) {
		this.task = task;
	}
	
	@Override
	public void startUp() {
		task.setState(task.getUnderwayState());
		System.out.println("啟動成功,任務 已更新至運作中狀态");
	}

	@Override
	public void writeContent() {
		System.out.println("寫入失敗,任務尚未啟動");

	}

	@Override
	public void solve() {
		System.out.println("操作失敗,任務尚未啟動");
	}

	@Override
	public void close() {
		System.out.println("操作失敗,任務尚未啟動");
	}

}
           
package com.hirain.chc.state.demo.state;

import com.hirain.chc.state.demo.Task;

public class UnderwayState implements ITaskState {

	Task task;
	
	public UnderwayState(Task task) {
		this.task = task;
	}

	@Override
	public void startUp() {
		System.out.println("操作失敗,任務已處于運作中狀态");
	}

	@Override
	public void writeContent() {
		System.out.println("寫入成功");
	}

	@Override
	public void solve() {
		System.out.println("操作成功,任務已更新至已解決狀态");
		task.setState(task.getResolvedState());
	}

	@Override
	public void close() {
		System.out.println("操作失敗,任務正處于運作中狀态");
	}

}
           
package com.hirain.chc.state.demo.state;

import com.hirain.chc.state.demo.Task;

public class ResolvedState implements ITaskState {

	Task task;
	
	public ResolvedState(Task task) {
		this.task = task;
	}

	@Override
	public void startUp() {
		System.out.println("操作失敗,任務已處于已解決狀态");
	}

	@Override
	public void writeContent() {
		System.out.println("操作失敗,任務已處于已解決狀态");
	}

	@Override
	public void solve() {
		System.out.println("操作失敗,任務已處于已解決狀态");
	}

	@Override
	public void close() {
		System.out.println("操作成功,任務已更新至已關閉狀态");
		task.setState(task.getClosedState());
	}

}
           
package com.hirain.chc.state.demo.state;

import com.hirain.chc.state.demo.Task;

public class ClosedState implements ITaskState {

	Task task;
	
	public ClosedState(Task task) {
		this.task = task;
	}

	@Override
	public void startUp() {
		System.out.println("操作失敗,任務已處于已關閉狀态");
	}

	@Override
	public void writeContent() {
		System.out.println("操作失敗,任務已處于已關閉狀态");
	}

	@Override
	public void solve() {
		System.out.println("操作失敗,任務已處于已關閉狀态");
	}

	@Override
	public void close() {
		System.out.println("操作失敗,任務已處于已關閉狀态");
	}

}
           

測試類:

package com.hirain.chc.state.demo;

/**
 * 用戶端應用
 * 
 * @author
 * 
 */
public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Task task = new Task();
		task.close();
		task.startUp();
		task.close();
		task.solve();
		task.close();
	}
}
           

這就是按照狀态模式來實作的,雖然類變多了,但是減少了複雜的if else ,同時程式也更容易拓展了,如果我們現在要增加一種"挂起"狀态,那麼需要更改的地方就是Task類

的變量與構造方法,然後增加一個"挂起"狀态類就可以了。雖然對開閉原則的支援不是很好,但相比之下還是比較容易接受的。

優缺點分析:

優點:

将與特定狀态相關的行為局部化,并且将不同狀态的行為分割開來,符合類的單一職責原則。

消除了複雜的if else判斷語句。

缺點:

增加了類的個數。

對開閉原則的支援并不好,新增狀态類的時候需要修改負責狀态轉換部分的源碼。

狀态模式與政策模式的比較:

狀态模式将各個狀态所對應的行為分離開來,即對于不同的狀态,由不同的子類負責具體操作,不同狀态的切換由子類實作;

而政策模式是直接依賴注入到Context類的參數進行選擇政策,不存在切換狀态的操作。