如果您不瞭解TCP的連線方式,在看
Gof 的書介紹State模式時,大概會看得一頭霧水吧!TCP的連線狀態(tài)圖,光是要瞭解就要花點精神了,它的連線狀態(tài)很多,用來說明狀態(tài)模式確實很適合,但不適合教導(dǎo)初學(xué)模式的人。
由簡單的開始會比較好理解狀態(tài)模式的作用,先來看一個例子,如果您有一個只能順時針轉(zhuǎn)動的瓦斯開關(guān),轉(zhuǎn)動一次的狀態(tài)為off、 small fire、medium fire與large fire,您如何在程式中控制狀態(tài)的變化與行為呢?一個最簡單的方式就是用if..else或是switch流程來控制,例如:
public class State {
private int state;
public State() {
state = 0;
}
public void switchFire() {
if (state == 0) {
state = 1;
System.out.println( "small fire" );
} else if (state == 1) {
state = 2;
System.out.println( "medium fire" );
} else if (state == 2) {
state = 3;
System.out.println( "large fire" );
} else {
state = 0;
System.out.println( "turning off" );
}
}
}
public class Main {
public static void main(String[] args) {
State state = new State();
state.switchFire();
state.switchFire();
state.switchFire();
state.switchFire();
}
}
這個方法很簡單,每個人都會,但如果您的狀態(tài)變化並不是流水式的變化,而是像TCP連線狀態(tài)一樣,會是一個網(wǎng)絡(luò)圖的時候,用 if...else或switch來寫的話,您的程式就會亂的不像話了;來考慮如何讓物件控制自己的狀態(tài)轉(zhuǎn)換與所應(yīng)表現(xiàn)的行為,這個程式可以這樣改寫:
public interface IState {
public void switchFire(FireSwitch sw);
}
public class OffState implements IState {
public void switchFire(FireSwitch sw) {
sw.setState(new SmallState());
System.out.println( "small fire" );
}
}
public class SmallState implements IState {
public void switchFire(FireSwitch sw) {
sw.setState(new MediumState());
System.out.println( "medium fire" );
}
}
public class MediumState implements IState {
public void switchFire(FireSwitch sw) {
sw.setState(new LargeState());
System.out.println( "large fire" );
}
}
public class LargeState implements IState {
public void switchFire(FireSwitch sw) {
sw.setState(new OffState());
System.out.println( "off fire" );
}
}
public class FireSwitch {
private State current;
public FireSwitch() {
current = new OffState();
}
public void setState(State s) {
current = s;
}
public void switchFire() {
current.switchFire(this);
}
}
public class Main {
public static void main(String[] args) {
FireSwitch fireSwitch = new FireSwitch();
fireSwitch.switchFire();
fireSwitch.switchFire();
fireSwitch.switchFire();
fireSwitch.switchFire();
}
}
程式執(zhí)行結(jié)果與上一個例子是一樣的,但這次並沒有用流程控制來進(jìn)行狀態(tài)轉(zhuǎn)換,而由物件自行控制自己的狀態(tài),與必須表現(xiàn)的行為,這個方式就是State 模式,將這個例子的 UML 類別結(jié)構(gòu)畫出就如下所示:
再進(jìn)一步考慮開關(guān)可以順時針與逆時針轉(zhuǎn)動,這時如果您仍以if...else或switch來寫,就會讓流程顯示複雜,來看看如何使用狀態(tài)模式來撰寫:
public interface IState {
public void switchClockWise(FireSwitch sw);
public void switchCountClock(FireSwitch sw);
}
public class OffState implements IState {
public void switchClockWise(FireSwitch sw) {
sw.setState(new SmallState());
System.out.println("small fire");
}
public void switchCountClock(FireSwitch sw) {
sw.setState(new LargeState());
System.out.println("large fire");
}
}
public class SmallState implements IState {
public void switchClockWise(FireSwitch sw) {
sw.setState(new MediumState());
System.out.println("medium fire");
}
public void switchCountClock(FireSwitch sw) {
sw.setState(new OffState());
System.out.println("off fire");
}
}
public class MediumState implements IState {
public void switchClockWise(FireSwitch sw) {
sw.setState(new LargeState());
System.out.println("large fire");
}
public void switchCountClock(FireSwitch sw) {
sw.setState(new SmallState());
System.out.println("small fire");
}
}
public class LargeState implements State {
public void switchClockWise(FireSwitch sw) {
sw.setState(new OffState());
System.out.println("off fire");
}
public void switchCountClock(FireSwitch sw) {
sw.setState(new MediumState());
System.out.println("medium fire");
}
}
public class FireSwitch {
private State current;
public FireSwitch() {
current = new OffState();
}
public void setState(State s) {
current = s;
}
public void switchClockWise() {
current.switchClockWise(this);
}
public void switchCountClock() {
current.switchCountClock(this);
}
}
public class Main {
public static void main(String[] args) {
FireSwitch fireSwitch = new FireSwitch();
fireSwitch.switchClockWise();
fireSwitch.switchClockWise();
fireSwitch.switchClockWise();
fireSwitch.switchClockWise();
System.out.println();
fireSwitch.switchCountClock();
fireSwitch.switchCountClock();
fireSwitch.switchCountClock();
fireSwitch.switchCountClock();
}
}
接下來您可以任意的轉(zhuǎn)動開關(guān)了,無論是順時針轉(zhuǎn)動或是逆時針轉(zhuǎn)動,狀態(tài)的轉(zhuǎn)換都由物件自己來表現(xiàn),這是雙向狀態(tài)轉(zhuǎn)換下的例子,如果一個狀態(tài)可能轉(zhuǎn)換至三個以上的狀態(tài),使用State模式就更可以看出它的好處了,就像Gof的TCP連線例子一樣,如果您瞭解TCP連線,可以看看原書是如何實現(xiàn)TCP連線之間的狀態(tài)轉(zhuǎn)換的。
State模式的UML結(jié)構(gòu)圖如下: