1. 程式人生 > >狀態模式----State Pattern

狀態模式----State Pattern

當一個物件內在狀態改變時允許其改變行為,這個物件看起來像是改變了其類 。 Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. [GoF, p305],也就是說狀態模式封裝的非常好,狀態的變更引起了行為的變更,從外部看起來就好像這個物件對應的類發生了改變一樣。

細想狀態變更的實質就是一個狀態與狀態二維的關係表。電梯例子,通過狀態的變化形成二維的表格,電梯門關閉、電梯門開啟、電梯上下運載、電梯停止。

如果通過一個個遍歷狀態通過if  esle 完成程式碼不利於程式碼維護。



public abstract class LiftState {

    protected Context context;

    public void setContext(Context _context) {
        this.context = _context;
    }

    public abstract void open();

    public abstract void close();

    public abstract void run();

    public abstract void stop();
}
public class Context {

    // 定義出所有的電梯狀態
    public final static OpenningState openningState = new OpenningState();
    public final static ClosingState closeingState = new ClosingState();
    public final static RunningState runningState = new RunningState();
    public final static StoppingState stoppingState = new StoppingState();

    // 定一個當前電梯狀態
    private LiftState liftState;

    public LiftState getLiftState() {
        return liftState;
    }

    public void setLiftState(LiftState liftState) {
        this.liftState = liftState;
        // 把當前的環境通知到各個實現類中
        this.liftState.setContext(this);
    }

    public void open() {
        this.liftState.open();
    }

    public void close() {
        this.liftState.close();
    }

    public void run() {
        this.liftState.run();
    }

    public void stop() {
        this.liftState.stop();
    }
}

public class ClosingState extends LiftState { 
 
  //電梯門關閉,這是關閉狀態要實現的動作 
  @Override 
  public void close() { 
  System.out.println("電梯門關閉..."); 
 
 } 
 
  //電梯門關了再開啟,逗你玩呢,那這個允許呀 
  @Override 
  public void open() { 
   super.context.setLiftState(Context.openningState);  //置為門敞狀態 
   super.context.getLiftState().open(); 
 } 
 
  //電梯門關了就跑,這是再正常不過了 
  @Override 
  public void run() { 
   super.context.setLiftState(Context.runningState); //設定為執行狀態; 
   super.context.getLiftState().run(); 
 } 
 
  //電梯門關著,我就不按樓層 
  @Override 
  public void stop() { 
   super.context.setLiftState(Context.stoppingState);  //設定為停止狀態; 
   super.context.getLiftState().stop(); 
 } 
} 


public class RunningState extends LiftState { 
  
  //電梯門關閉?這是肯定了 
  @Override 
  public void close() { 
   //do nothing 
 } 
 
  //執行的時候開電梯門?你瘋了!電梯不會給你開的 
  @Override 
  public void open() { 
   //do nothing 
 } 
 
  //這是在執行狀態下要實現的方法 
  @Override 
  public void run() { 
  System.out.println("電梯上下跑..."); 
 } 
 
  //這個事絕對是合理的,光執行不停止還有誰敢做這個電梯?!估計只有上帝了 
  @Override 
  public void stop() { 
   super.context.setLiftState(Context.stoppingState); //環境設定為停止狀態; 
   super.context.getLiftState().stop(); 
 } 
 
}


public class StoppingState extends LiftState {

    @Override
    public void close() {
        // do nothing;
    }

    @Override
    public void open() {
        super.context.setLiftState(Context.openningState);
        super.context.getLiftState().open();
    }

    @Override
    public void run() {
        super.context.setLiftState(Context.runningState);
        super.context.getLiftState().run();
    }

    @Override
    public void stop() {
        System.out.println("電梯停止了...");
    }
}


public class Client { 
  
  public static void main(String[] args) { 
  Context context = new Context(); 
  context.setLiftState(new ClosingState()); 
   
  context.open(); 
  context.close(); 
  context.run(); 
  context.stop(); 
 } 
} 

優點:
 首先是避免了過多的 swith…case或者if..else語句的使用,避免了程式的複雜性;
 其次是很好的使用體現了開閉原則和單一職責原則,每個狀態都是一個子類,增加狀態就增加子類,你要修改狀態,你只修改一個子類就可以了;
 最後一個好處就是封裝性非常好,這也是狀態模式的基本要求,狀態變換放置到了類的內部來實現,外部的呼叫不用知道類內部如何實現狀態和行為的變換。 
缺點:子類會太多,也就是類膨脹。

工作流中 Activity(節點)有初始化狀態(Initialized State)、掛起狀態(Suspended State)、完成狀態(Completed State)等等,流程例項這麼多狀態,狀態怎麼管理呢?通過狀態機(State Machine)來管理, 狀態機就是 Context類的擴充套件版!