全文一共1543字,預計閱讀時間10分鍾
定義:
狀態模式(State),當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類。
只看這個定義的話,想必會一頭霧水,其實狀態模式解決的問題是:
當控制了一個對象狀態轉換的表達式過於復雜時,我們可以把狀態的判斷邏輯轉移到表示不同狀態的一系列的類中。這樣做可以使復雜的判斷邏輯簡化,同時使類的職責更加單一。
實例:
假設每一個程序員會對應一個經驗值(empiricalValue),我們會根據這個程序員的經驗值,來評定這個程序員的職稱,如MT,開發助理,初級程序員,中級程序員,高級程序員,專家。那么讓你來完成這個程序,你會如何設計你的代碼呢?
相信有一部分人會寫出和我一樣的代碼:
** * 程序員類. * * @author jialin.li * @date 2019-12-30 17:38 */ public class Programmer { private int empiricalValue; public void setEmpiricalValue(int empiricalValue) { this.empiricalValue = empiricalValue; } public void evaluate() { if(empiricalValue < 100){ System.out.println("MT"); }else if(empiricalValue < 200){ System.out.println("開發助理"); }else if(empiricalValue < 300){ System.out.println("初級程序員"); }else if(empiricalValue < 400){ System.out.println("中級程序員"); }else if(empiricalValue < 500){ System.out.println("高級程序員"); }else if(empiricalValue < 600){ System.out.println("技術專家"); } } }
/** * 客戶端. * * @author jialin.li * @date 2019-12-30 18:28 */ public class Main { public static void main(String[] args) { Programmer programmer = new Programmer(); programmer.setEmpiricalValue(50); programmer.evaluate(); programmer.setEmpiricalValue(150); programmer.evaluate(); programmer.setEmpiricalValue(250); programmer.evaluate(); programmer.setEmpiricalValue(350); programmer.evaluate(); programmer.setEmpiricalValue(450); programmer.evaluate(); programmer.setEmpiricalValue(550); programmer.evaluate(); } }
結果:
MT
開發助理
初級程序員
中級程序員
高級程序員
技術專家
這樣的代碼有什么問題?
首先,evaluate方法充斥着大量的if/else,這個時候就要警惕,因為大量的if/else往往代表了該代碼不符合開閉原則,每次修改或者新增條件,都會對原來的代碼產生影響。
其次,evaluate方法比較長,在面向對象編程中,方法的設計應該是短小且功能單一的,較長的方法往往意味着該方法不符合單一職責原則,或者是需要進行抽象。
實際上,這段代碼是用面向對象語言寫出的面向過程的代碼。這也是軟件開發中常見的誤區之一,並不是使用面向對象語言,寫出的代碼就是面向對象代碼。
這個時候應該如何對上述代碼進行優化?這就引出了我們今天要介紹的設計模式:狀態模式,狀態模式的結構相對簡單,但是用法卻十分巧妙:

這里將狀態模式對應到我們的實例中,對經驗值的判定和處理,可以被封裝成一個個ConcreteState,將他們的處理方法抽象出來,就是State,而Context則起到一個維護當前狀態的作用,接下來是我們修改后的代碼:
代碼:
/** * 狀態處理接口. * * @author jialin.li * @date 2019-12-30 19:23 */ public interface State { void handle(Programmer programmer); }
/** * 程序員類. * * @author jialin.li * @date 2019-12-30 17:38 */ public class Programmer { /** * 經驗值 */ private int empiricalValue; /** * 當前狀態 */ private State state = new MTState(); public void setEmpiricalValue(int empiricalValue) { this.empiricalValue = empiricalValue; } public int getEmpiricalValue() { return empiricalValue; } public void setState(State state) { this.state = state; } public void handle() { state.handle(this); } }
/** * MT. * * @author jialin.li * @date 2019-12-30 19:30 */ public class MTState implements State { @Override public void handle(Programmer programmer) { int empiricalValue = programmer.getEmpiricalValue(); if (empiricalValue < 100) { System.out.println("MT"); } else { State juniorProgrammer = new JuniorProgrammer(); programmer.setState(juniorProgrammer); juniorProgrammer.handle(programmer); } } }
/** * 助理程序員. * * @author jialin.li * @date 2019-12-30 19:33 */ public class ProgrammerAssistant implements State{ @Override public void handle(Programmer programmer) { int empiricalValue = programmer.getEmpiricalValue(); if(empiricalValue < 200){ System.out.println("開發助理"); }else{ JuniorProgrammer juniorProgrammer = new JuniorProgrammer(); programmer.setState(juniorProgrammer); juniorProgrammer.handle(programmer); } } }
/** * 初級程序員. * * @author jialin.li * @date 2019-12-30 19:31 */ public class JuniorProgrammer implements State { @Override public void handle(Programmer programmer) { int empiricalValue = programmer.getEmpiricalValue(); if (empiricalValue < 300) { System.out.println("初級程序員"); } else { State middleProgrammer = new MiddleProgrammer(); programmer.setState(middleProgrammer); middleProgrammer.handle(programmer); } } }
/** * 中級程序員. * * @author jialin.li * @date 2019-12-30 19:32 */ public class MiddleProgrammer implements State { @Override public void handle(Programmer programmer) { int empiricalValue = programmer.getEmpiricalValue(); if (empiricalValue < 400) { System.out.println("中級程序員"); } else { SeniorProgrammer seniorProgrammer = new SeniorProgrammer(); programmer.setState(seniorProgrammer); seniorProgrammer.handle(programmer); } } }
/** * 高級程序員. * * @author jialin.li * @date 2019-12-30 19:34 */ public class SeniorProgrammer implements State { @Override public void handle(Programmer programmer) { int empiricalValue = programmer.getEmpiricalValue(); if (empiricalValue < 500) { System.out.println("高級程序員"); } else { Professor professor = new Professor(); programmer.setState(professor); professor.handle(programmer); } } }
/** * @author jialin.li * @date 2019-12-30 19:35 */ public class Professor implements State { @Override public void handle(Programmer programmer) { System.out.println("技術專家"); } }
/** * @author jialin.li * @date 2019-12-30 19:35 */ public class Professor implements State { @Override public void handle(Programmer programmer) { System.out.println("技術專家"); } }
/** * 客戶端. * * @author jialin.li * @date 2019-12-30 19:36 */ public class Main { public static void main(String[] args) { Programmer programmer = new Programmer(); programmer.setEmpiricalValue(50); programmer.handle(); programmer.setEmpiricalValue(150); programmer.handle(); programmer.setEmpiricalValue(250); programmer.handle(); programmer.setEmpiricalValue(350); programmer.handle(); programmer.setEmpiricalValue(450); programmer.handle(); programmer.setEmpiricalValue(550); programmer.handle(); } }
結果:
MT
初級程序員
初級程序員
中級程序員
高級程序員
技術專家
可以看出客戶端代碼基本沒有變化,服務端代碼變得更加靈活了。
在Tomcat中,有一個LifecycleState枚舉類,用於描述組件的生命周期狀態,狀態變更的時候,會進行上一個狀態的判斷,從而確定本次狀態變更是否合法,這種設計也可以用在我們的狀態模式中。
@Override public final synchronized void init() throws LifecycleException { if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.BEFORE_INIT_EVENT); } try { setStateInternal(LifecycleState.INITIALIZING, null, false); initInternal(); setStateInternal(LifecycleState.INITIALIZED, null, false); } catch (Throwable t) { handleSubClassException(t, "lifecycleBase.initFail", toString()); } }
private void invalidTransition(String type) throws LifecycleException { String msg = sm.getString("lifecycleBase.invalidTransition", type, toString(), state); throw new LifecycleException(msg); }
其實萬變不離其宗,在設計模式中,這個宗指的就是各種設計原則。學習設計模式的時候,要時刻聯系設計原則,有一種說法是,真正精通設計模式的時候,是忘記所有的設計模式(有一種張無忌學太極劍的感覺)。
期待您的關注、推薦、收藏,同時也期待您的糾錯和批評。