設計模式之(十七)狀態模式State

由簡單的開始會比較好理解狀態模式的作用,先來看一個例子,如果您有一個只能順時針轉動的瓦斯開關,轉動一次的狀態爲off、 small fire、medium fire與large fire,您如何在程式中控制狀態的變化與行爲呢?一個最簡單的方式就是用if..else或是switch流程來控制,例如:
  • State.java
  • 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" ); 
            } 
        } 
    }  

  • Main.java
  • public class Main { 
         public static void main(String[] args) { 
            State state = new State();
    
            state.switchFire(); 
            state.switchFire(); 
            state.switchFire(); 
            state.switchFire(); 
        } 
    } 

這個方法很簡單,每個人都會,但如果您的狀態變化並不是流水式的變化,而是像TCP連線狀態一樣,會是一個網絡圖的時候,用 if...else或switch來寫的話,您的程式就會亂的不像話了;來考慮如何讓物件控制自己的狀態轉換與所應表現的行爲,這個程式可以這 樣改寫:
  • IState.java
  • public interface IState { 
        public void switchFire(FireSwitch sw); 
    } 

  • OffState
  • public class OffState implements IState { 
        public void switchFire(FireSwitch sw) { 
            sw.setState(new SmallState()); 
            System.out.println( "small fire" ); 
        } 
    } 

  • SmallState.java
  • public class SmallState implements IState { 
        public void switchFire(FireSwitch sw) { 
            sw.setState(new MediumState()); 
            System.out.println( "medium fire" ); 
        } 
    }  

  • MediumState.java
public class MediumState implements IState { 
    public void switchFire(FireSwitch sw) { 
        sw.setState(new LargeState()); 
        System.out.println( "large fire" ); 
    } 
}  

  • LargeState.java
public class LargeState implements IState { 
    public void switchFire(FireSwitch sw) { 
        sw.setState(new OffState()); 
        System.out.println( "off fire" ); 
    } 
}  

  • FireSwitch.java
public class FireSwitch { 
    private State current;

    public FireSwitch() { 
        current = new OffState(); 
    }

    public void setState(State s) { 
        current = s; 
    }

    public void switchFire() { 
        current.switchFire(this); 
    }
} 

  • Main.java
public class Main { 
    public static void main(String[] args) { 
        FireSwitch fireSwitch = new FireSwitch();
        fireSwitch.switchFire(); 
        fireSwitch.switchFire(); 
        fireSwitch.switchFire(); 
        fireSwitch.switchFire(); 
    } 
} 

程式執行結果與上一個例子是一樣的,但這次並沒有用流程控制來進行狀態轉換,而由物件自行控制自己的狀態,與必須表現的行爲,這個方式就是State 模式,將這個例子的 UML 類別結構畫出就如下所示:


再進一步考慮開關可以順時針與逆時針轉動,這時如果您仍以if...else或switch來寫,就會讓流程顯示覆雜,來看看如何使用狀態模式來撰 寫: 
  • IState.java
public interface IState { 
    public void switchClockWise(FireSwitch sw); 
    public void switchCountClock(FireSwitch sw); 
}  

  • OffState.java
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"); 
    } 
}  

  • SmallState.java
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"); 
    } 
}  

  • MediumState.java
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"); 
    } 
}  

  • LargeState.java
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"); 
    } 
}  

  • FireSwitch.java
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); 
    } 
}  

  • Main.java
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(); 
    } 
} 

接下來您可以任意的轉動開關了,無論是順時針轉動或是逆時針轉動,狀態的轉換都由物件自己來表現,這是雙向狀態轉換下的例子, 如果一個狀態可能轉換至三個以上的狀態,使用State模式就更可以看出它的好處了,就像Gof的TCP連線例子一樣,如果您瞭解TCP連線,可以看看原 書是如何實現TCP連線之間的狀態轉換的。 

State模式的UML結構圖如下: 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章