目的
讓物件的外顯行為隨內部狀態的改變而改變,彷彿連類別也變了似的。
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
適用
- 當物件的行為取決於它的狀態,連執行期行為也得隨狀態而改變時。
- 如果某些操作裡面,依據物件狀態而做的條件判斷式太過龐大時。狀態通常會以enum表示,許多操作裡面也常會有相同的條件判斷結構。State Pattern將每一個條件分支抽取成獨立的類別,以便將物件狀態也視為另一個獨立的物件,可獨立改變。
結構及成員
Collaborations:
Context將與狀態有關的訊息委託給目前持有的ConcreteState去處理。
Context可能把自己當作傳參傳給State。
Context是主要的對外窗口。Client可用State物件來設定Context的組態,設好之後便不再直接插手State物件。
可令Context或ConcreteState的子類別決定在什麼情況下該切換成什麼狀態。
影響結果
好處
壞處
實作
誰來定義狀態轉移邏輯?
- 若條件是固定的,可以全寫在Context裡;而彈性更好的做法,是讓State的子類別決定自己的下一個State是誰、何時才做轉移。
- Context需增加一個讓State物件可以設定Context的State的方法。
- 只要定義新的State子類別,就能修改或擴充狀態轉移邏輯。但缺點是State子類別需要知道至少一個其他的State子類別,增加子類別之間的實作相依性。
State物件的生與死?
- 取捨:
- 有需要時才產生State物件,用完即砍。
- 事先件立好,事後絕不殺掉。
- 若在Run-time時不知道處於哪些狀態,且Context不常改變狀態時,第一種做法較好。
- 若State物件含有大量資訊,且狀態常常改變,就會想避免一再刪除State物件,就該用第二種做法。
使用動態繼承
若能在Run-time動態改變物件所隸屬的類別,就能針對特定訊息改變自身行為。
Example: Cruise Control System
public abstract class State {
State[] states;
abstract void leverDown(CruiseControl c);
abstract void leverUpAndHold(CruiseControl c);
abstract void leverDownAndHold(CruiseControl c);
abstract void leverReleased(CruiseControl c);
abstract void leverPulled(CruiseControl c);
abstract void brakeApplied(CruiseControl c);
abstract void leverUp(CruiseControl c);
abstract void onOffButtonPressed(CruiseControl c);
void setDesiredSpeed(CruiseControl c) {
System.out.println("setDesiredSpeed");
}
}
public class CruiseDeactivated extends State {
@Override
void onOffButtonPressed(CruiseControl c) {
// TODO Auto-generated method stub
System.out.println("CruiseDeactivated change to CruiseActivated");
System.out.println("CruiseDeactivated change to CruisingCancelled");
c.setState(new CruisingCancelled());
}
@Override
void leverDown(CruiseControl c) {}
@Override
void leverUp(CruiseControl c) {}
@Override
void brakeApplied(CruiseControl c) {}
@Override
void leverDownAndHold(CruiseControl c) {}
@Override
void leverUpAndHold(CruiseControl c) {}
@Override
void leverPulled(CruiseControl c) {}
@Override
void leverReleased(CruiseControl c) {}
@Override
void setDesiredSpeed(CruiseControl c) {}
}
public class CruiseActivated extends State {
@Override
void onOffButtonPressed(CruiseControl c) {
// TODO Auto-generated method stub
System.out.println("CruiseActivated change to CruiseDeactivated");
c.setState(new CruiseDeactivated());
}
@Override
void leverDown(CruiseControl c) {}
@Override
void leverUp(CruiseControl c) {}
@Override
void brakeApplied(CruiseControl c) {}
@Override
void leverDownAndHold(CruiseControl c) {}
@Override
void leverUpAndHold(CruiseControl c) {}
@Override
void leverPulled(CruiseControl c) {}
@Override
void leverReleased(CruiseControl c) {}
}
public class Cruising extends CruiseActivated {
@Override
void leverDown(CruiseControl c) {
System.out.println("Cruising change to Cruising");
setDesiredSpeed(c);
c.setState(new Cruising());
}
@Override
void brakeApplied(CruiseControl c) {
System.out.println("Cruising change to CruisingCancelled");
c.setState(new CruisingCancelled());
}
@Override
void leverDownAndHold(CruiseControl c) {
System.out.println("Cruising change to DecreasingSpeed");
c.setState(new DecreasingSpeed());
}
@Override
void leverUpAndHold(CruiseControl c) {
System.out.println("Cruising change to IncreasingSpeed");
c.setState(new IncreasingSpeed());
}
@Override
void leverPulled(CruiseControl c) {
System.out.println("Cruising change to CruisingCancelled");
c.setState(new CruisingCancelled());
}
}
public class CruisingCancelled extends CruiseActivated {
@Override
void leverDown(CruiseControl c) {
System.out.println("CruisingCancelled change to Cruising");
setDesiredSpeed(c);
c.setState(new Cruising());
}
@Override
void leverUp(CruiseControl c) {
System.out.println("CruisingCancelled change to Cruising");
c.setState(new Cruising());
}
@Override
void leverDownAndHold(CruiseControl c) {
System.out.println("CruisingCancelled change to DecreasingSpeed");
c.setState(new DecreasingSpeed());
}
@Override
void leverUpAndHold(CruiseControl c) {
System.out.println("CruisingCancelled change to IncreasingSpeed");
c.setState(new IncreasingSpeed());
}
}
public class DecreasingSpeed extends CruiseActivated{
@Override
void leverReleased(CruiseControl c){
System.out.println("DecreasingSpeed change to Cruising");
setDesiredSpeed(c);
c.setState(new Cruising());
}
}
public class IncreasingSpeed extends CruiseActivated{
void leverReleased(CruiseControl c){
System.out.println("IncreasingSpeed change to Cruising");
c.setState(new Cruising());
}
}
public class CruiseControl {
State state;
public CruiseControl() {
// TODO Auto-generated constructor stub
this.state = new CruiseDeactivated();
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
void onOffButtonPressed() {
state.onOffButtonPressed(this);
}
void leverDown() {
state.leverDown(this);
}
void leverUp() {
state.leverUp(this);
}
void brakeApplied() {
state.brakeApplied(this);
}
void leverDownAndHold() {
state.leverDownAndHold(this);
}
void leverUpAndHold() {
state.leverUpAndHold(this);
}
void leverPulled() {
state.leverPulled(this);
}
void leverReleased() {
state.leverReleased(this);
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
CruiseControl cruiseControl = new CruiseControl();
cruiseControl.onOffButtonPressed();
cruiseControl.leverDown();
cruiseControl.onOffButtonPressed();
}
}
Result:
CruiseDeactivated change to CruiseActivated
CruiseDeactivated change to CruisingCancelled
CruisingCancelled change to Cruising
setDesiredSpeed
CruiseActivated change to CruiseDeactivated