我們開發(fā)項(xiàng)目的過程中通常會碰到一種需求,某個(gè)對象包含多種狀態(tài)變化的可能性,隨著狀態(tài)的變化行為也執(zhí)行不同的動作。

以電梯來舉例:
電梯最基本具備 開門、關(guān)門、上下走動、停止 這四種狀態(tài)。狀態(tài)與狀態(tài)之間有約束的關(guān)系,比如只能在關(guān)門的情況下才能走動。
所以用傳統(tǒng)面向過程的解決方案,就必須要用狀態(tài)值來標(biāo)記每一種狀態(tài),然后用多個(gè)if else語句來判斷狀態(tài)的執(zhí)行順序,每當(dāng)增加一個(gè)新狀態(tài)的時(shí)候就需要重新修改代碼,這樣就不符合開閉原則。
我們來看下如何用狀態(tài)模式來優(yōu)雅的處理問題。
public abstract class LiftState
{
protected Context context;
public void setContext(Context context)
{
this.context = context;
}
public abstract void open(); //電梯開門
public abstract void close(); //電梯關(guān)門
public abstract void stop(); //電梯停止
public abstract void run(); //電梯上下走動
}
//電梯處于開門狀態(tài)
public class OpenState extends LiftState
{
@Override
public void open() {
//本身就處于開門狀態(tài)
//do nothing
}
@Override
public void close() {
super.context.setLiftStatus(Context.closeState); //切換至關(guān)門狀態(tài)
super.context.getLiftStatus().close(); //委托關(guān)門狀態(tài)類 來執(zhí)行具體的關(guān)門動作
}
@Override
public void stop() {
//電梯開門狀態(tài)時(shí),本身就停止的
//do nothing
}
@Override
public void run() {
//電梯開門狀態(tài)時(shí),不能走動
//do nothing
}
}
//電梯走動狀態(tài)
public class RunState extends LiftState
{
@Override
public void open() {
//走動狀態(tài)不能開門
//do nothing
}
@Override
public void close() {
//走動狀態(tài)本身就是處于關(guān)門狀態(tài)
//do nothing
}
@Override
public void stop() {
super.context.setLiftStatus(Context.stopState); //切換至停止?fàn)顟B(tài)
super.context.getLiftStatus().stop(); //委托停止?fàn)顟B(tài)類 來執(zhí)行具體的停止動作
}
@Override
public void run() {
//本身就處于走動狀態(tài)
//do nothing
}
}
//電梯關(guān)門狀態(tài)類
public class CloseState extends LiftState
{
@Override
public void open() {
super.context.setLiftStatus(Context.openStatus); //切換至開門狀態(tài)
super.context.getLiftStatus().open(); //委托開門狀態(tài)類 來執(zhí)行具體的開門動作
}
@Override
public void close() {
}
@Override
public void stop() {
}
@Override
public void run() {
super.context.setLiftStatus(Context.runState); //切換至走動狀態(tài)
super.context.getLiftStatus().run(); //委托走動狀態(tài)類 來執(zhí)行具體的走動動作
}
}
//電梯停止?fàn)顟B(tài)類
public class StopState extends LiftState
{
@Override
public void open() {
super.context.setLiftStatus(Context.openStatus); //切換至開門狀態(tài)
super.context.getLiftStatus().open(); //委托開門狀態(tài)類 來執(zhí)行具體的走動動作
}
@Override
public void close() {
}
@Override
public void stop() {
}
@Override
public void run() {
super.context.setLiftStatus(Context.runState); //切換至走動狀態(tài)
super.context.getLiftStatus().run(); //委托走動狀態(tài)類 來執(zhí)行具體的走動動作
}
}
public class Context
{
public final static OpenState openStatus = new OpenState();
public final static CloseState closeState = new CloseState();
public final static RunState runState = new RunState();
public final static StopState stopState = new StopState();
private LiftState curStatus; //當(dāng)前電梯狀態(tài)
//設(shè)置電梯狀態(tài),并且把當(dāng)前上下文的引用傳遞給具體的狀態(tài)類,具體的狀態(tài)類通過上下文的引用切換不同的狀態(tài)。
public void setLiftStatus(LiftState liftStatus)
{
curStatus = liftStatus;
liftStatus.setContext(this);
}
//獲取當(dāng)前的電梯狀態(tài)
public LiftState getLiftStatus()
{
return curStatus;
}
public void open()
{
curStatus.open();
}
public void close()
{
curStatus.close();
}
public void stop()
{
curStatus.stop();
}
public void run()
{
curStatus.run();
}
}
//客戶端類
public class Client
{
public static void main()
{
Context context = new Context();
context.setLiftStatus(new CloseState()); //電梯初始化為關(guān)門狀態(tài)
context.open();
context.close();
context.run();
context.stop();
}
}
需要說明的一點(diǎn)是:
Context 是一個(gè)上下文角色,它的作用就是串聯(lián)各個(gè)狀態(tài)的過渡,在LifeState抽象類中我們定義了并把這個(gè)上下文組合進(jìn)來,病傳遞到了子類。也就是四個(gè)具體狀態(tài)類根據(jù)上下文的環(huán)境來決定如何進(jìn)行狀態(tài)的過渡。
狀態(tài)模式的優(yōu)點(diǎn):
首先,每個(gè)狀態(tài)類都很簡潔,同時(shí)也不需要用 if else 來判斷狀態(tài)的切換。
同時(shí),如果我們要新增狀態(tài)只需要創(chuàng)建新的狀態(tài)類即可,是通過擴(kuò)展而非修改的方式來實(shí)現(xiàn)功能,這是面向?qū)ο蟮囊粋€(gè)重要原則,開閉原則。非常完美。