设计模式-状态模式
今天學(xué)習(xí)了狀態(tài)模式,做個(gè)總結(jié)。 本文多出摘選自《設(shè)計(jì)模式之禪》,只留作學(xué)習(xí)復(fù)習(xí)只用。
為了更好地了解學(xué)習(xí)狀態(tài)模式,先認(rèn)識(shí)一個(gè)小例子——電梯。
舉個(gè)例子
電梯大家應(yīng)該都很熟悉,日常生活用得到,電梯的日常上下路邏輯大家應(yīng)該也很熟悉,就比如我們每個(gè)人去坐電梯,一個(gè)最簡(jiǎn)單最基礎(chǔ)的流程是:
一個(gè)人來(lái)到電梯前按按鈕上下樓——>電梯開(kāi)門——>進(jìn)去關(guān)上門按按鈕——>電梯運(yùn)行向上向下——>到達(dá)目的樓層,電梯停止。?
在上邊的一個(gè)基本流程中,包含了開(kāi)門,關(guān)門,停止,運(yùn)行四個(gè)基本動(dòng)作,包含了敞開(kāi)著門的狀態(tài),閉上門狀態(tài),停止不動(dòng)的狀態(tài),運(yùn)行狀態(tài),四個(gè)基礎(chǔ)狀態(tài),電梯的一個(gè)基本工作邏輯就是在作出行為之后,狀態(tài)不斷地進(jìn)行變換。?
接下來(lái),如果我們按照電梯的這個(gè)簡(jiǎn)單運(yùn)行邏輯設(shè)計(jì)一下電梯模型。
模型設(shè)計(jì)
模型設(shè)計(jì),最簡(jiǎn)單的就是設(shè)計(jì)一個(gè)電梯的抽象接口ILift,有四個(gè)基本動(dòng)作,開(kāi)門,關(guān)門,停止,運(yùn)行,再來(lái)幾個(gè)具體實(shí)現(xiàn)類Lift,再加一個(gè)實(shí)現(xiàn)類Client。
?這樣具體類去實(shí)現(xiàn)抽象接口的抽象方法就可以最簡(jiǎn)單的實(shí)現(xiàn)該模型。
但是我們知道,電梯運(yùn)行的話不是只有這幾個(gè)方法這么簡(jiǎn)單,他應(yīng)該包含著更復(fù)雜一點(diǎn)的邏輯,至少在上邊 基礎(chǔ)流程中,包含著四個(gè)狀態(tài)之間的不停轉(zhuǎn)換,以及夾雜在四個(gè)狀態(tài)變換中的電梯的基礎(chǔ)動(dòng)作行為。? 因此我們給電梯模型設(shè)計(jì)一下更復(fù)雜的邏輯。
更具體的模型
在開(kāi)始設(shè)計(jì)更具體的模型的時(shí)候,我們先把四個(gè)狀態(tài)和和四個(gè)動(dòng)作之間的關(guān)系理順。
如下表:?
| 開(kāi)門 | 關(guān)門 | 運(yùn)行 | 停止 | |
| 開(kāi)門狀態(tài) | x | √ | × | × | 
| 閉門狀態(tài) | √ | × | √ | √ | 
| 運(yùn)行狀態(tài) | × | × | × | √ | 
| 停止?fàn)顟B(tài) | √ | × | √ | × | 
(上表主對(duì)角線全部都是錯(cuò)號(hào),例如: 開(kāi)門狀態(tài)下不允許有開(kāi)門這個(gè)動(dòng)作,因?yàn)殚T已經(jīng)是開(kāi)著的)
這樣就比較清晰的來(lái)看出來(lái)他們之間的關(guān)系邏輯。
我們將這些邏輯豐富我們的電梯模型。
加個(gè)圖-狀態(tài)轉(zhuǎn)換圖?
基于上邊的表格我們作出狀態(tài)變換圖
定義四個(gè)狀態(tài)常數(shù)表示電梯狀態(tài),電梯的具體實(shí)現(xiàn)類中,定義四個(gè)函數(shù)來(lái)表示電梯的在不同狀態(tài)下的行為。
具體代碼?
抽象電梯接口ILift
interface ILift{public final static int open_state=1;public final static int close_state=2;public final static int run_state=3;public final static int stop_state=4;public void open();public void close();public void run();public void stop(); }具體電梯實(shí)現(xiàn)類
class Lift implements ILift{private int state;Lift(int s){state=s;}@Overridepublic void open() {switch (state){case open_state: break;case close_state: state=open_state;OpenD(); break;case run_state: break;case stop_state: state=open_state; OpenD(); break;}}@Overridepublic void close() {switch (state){case open_state: state=close_state;CloseD(); break;case close_state: break;case run_state: break;case stop_state: break;}}@Overridepublic void run() {switch (state){case open_state: break;case close_state: state=run_state; RunL(); break;case run_state: break;case stop_state: state=run_state; RunL();break;}}@Overridepublic void stop() {switch (state){case open_state: break;case close_state: state=stop_state; StopL(); break;case run_state: state=stop_state;StopL(); break;case stop_state: break;}}public void OpenD(){System.out.println("電梯開(kāi)門了!");}public void CloseD(){System.out.println("電梯關(guān)門了!");}public void RunL(){System.out.println("電梯開(kāi)始運(yùn)行!");}public void StopL(){System.out.println("電梯停下了!");} }場(chǎng)景測(cè)試類
public class Client {public static void main(String[] args) {ILift lift=new Lift(2);lift.open();lift.close();lift.run();lift.stop();}}效果圖
基于上述的我們改進(jìn)的電梯類,我們基本上實(shí)現(xiàn)了開(kāi)頭所講的那個(gè)基本電梯流程,包括四個(gè)狀態(tài)在四個(gè)行為下的不同切換。?
但是我們仔細(xì)思考一下,如果我們?cè)谙雽⑸鲜龅哪P驮O(shè)計(jì)的更具體一點(diǎn),比如添加一個(gè)電梯維修,電梯故障之類的功能,又要怎么去添加呢?
比如電梯故障這一方面,假如出現(xiàn)火災(zāi)之類的情況,那我們應(yīng)該怎么修改上述模型? 是不是要再添加一個(gè)電梯故障狀態(tài)? 或是在電梯四個(gè)動(dòng)作里再加上判斷條件來(lái)判斷電梯故障從而作出相應(yīng)動(dòng)作來(lái)處理他?無(wú)論選擇哪種決策,我們上邊實(shí)現(xiàn)的代碼就要大改一遍,這明顯不符合開(kāi)閉原則。這還僅僅只是一個(gè)模塊的加入,若是之后再加上電梯的通電模式,斷電模式等等,是不是要繼續(xù)不停修改代碼? 毫無(wú)疑問(wèn),這樣的工程開(kāi)發(fā)是低級(jí)的。
缺陷與更多的問(wèn)題
我們簡(jiǎn)單地總結(jié)一下上述模型所展現(xiàn)出來(lái)的缺陷。
1. 擴(kuò)展性與維護(hù)性較差?
就上述模型來(lái)講,無(wú)論是添加新?tīng)顟B(tài)還是修改現(xiàn)有狀態(tài)的邏輯,無(wú)疑,我們至少要修改上述open(),close()。。。之類方法內(nèi)容,這樣無(wú)論是擴(kuò)展性還是維護(hù)性都是極差的。
2. 判斷條件太多
上述代碼還有的一個(gè)問(wèn)題是判斷條件太多,只不過(guò)我們使用了switch來(lái)代替判斷條件,但很容易看出來(lái)我們代碼的冗余太多,判斷條件太多。
那是不是有一種方法來(lái)更好的實(shí)現(xiàn)電梯例子呢?
?于是我們就引出了設(shè)計(jì)模式中的狀態(tài)模式。
狀態(tài)模式
定義:當(dāng)一個(gè)對(duì)象內(nèi)在狀態(tài)改變時(shí)允許其改變行為,這個(gè)對(duì)象看起來(lái)像改變了其類。
是不是感覺(jué)上述定義有些抽象?
我們把上述電梯例子加入進(jìn)去,電梯模型有四個(gè)狀態(tài),四個(gè)動(dòng)作,每個(gè)狀態(tài)下有些行為允許去做,有些行為不被允許去做(具體參見(jiàn)上邊表格),就比如說(shuō)開(kāi)門狀態(tài),當(dāng)電梯處于開(kāi)門狀態(tài)下,她被允許的動(dòng)作僅僅只有關(guān)門,可是當(dāng)他通過(guò)關(guān)門去改變其狀態(tài)變?yōu)殚]門狀態(tài)時(shí),他被允許的動(dòng)作反而變成了開(kāi)門,運(yùn)行,停止三個(gè)動(dòng)作,也就是說(shuō)隨著電梯對(duì)象他的開(kāi)門狀態(tài)與閉門狀態(tài)切換時(shí),他的行為也發(fā)生了變化,這就是上述定義中的對(duì)象內(nèi)在狀態(tài)改變時(shí)允許其改變行為,更通俗一點(diǎn)就是每個(gè)狀態(tài)下都有其特殊的行為動(dòng)作,狀態(tài)發(fā)生變化,行為也就變得不同。
OK 了解了定義之后我們繼續(xù)看狀態(tài)模式。
狀態(tài)模式的核心就是封裝,狀態(tài)發(fā)生變更引起了行為變化,從外部看就像是對(duì)象對(duì)應(yīng)的類發(fā)生了變化一樣。通用類圖如下:
解釋一下這三個(gè)角色:
State :抽象狀態(tài)對(duì)象,負(fù)責(zé)對(duì)象狀態(tài)定義,并且封裝環(huán)境角色來(lái)實(shí)現(xiàn)狀態(tài)轉(zhuǎn)換。一般為抽象類或者接口。
ConcreteState:具體狀態(tài)角色, 需要完成兩個(gè)職責(zé),本狀態(tài)的行為管理和趨向狀態(tài)管理,就是本狀態(tài)下啊該干的事情,與本狀態(tài)該如何過(guò)渡到其他狀態(tài)。
Context : 環(huán)境角色,定義客戶端所需要的接口,并且負(fù)責(zé)具體狀態(tài)的切換。
了解了上述基本定義之后,我們應(yīng)該可以使用狀態(tài)模式來(lái)設(shè)計(jì)我們的電梯模型了。
改進(jìn)電梯模型
? ? ? ? 1.我們把電梯模型的狀態(tài)抽象成一個(gè)抽象類LiftState,每個(gè)具體的狀態(tài)類就是這個(gè)抽象類的子類,要是再有新的狀態(tài)加入進(jìn)來(lái)就可以再繼續(xù)添加子類,而不需要修改原有的狀態(tài)類,這樣就解決了可擴(kuò)展性問(wèn)題。
????????2. 在使用一個(gè)環(huán)境角色串聯(lián)起來(lái)這些狀態(tài),實(shí)現(xiàn)電梯狀態(tài)的切換。每個(gè)電梯狀態(tài)都有其特殊的行為動(dòng)作和不同的狀態(tài)轉(zhuǎn)換機(jī)制,這樣我們就可以通過(guò)切換環(huán)境狀態(tài)來(lái)實(shí)現(xiàn)每個(gè)電梯狀態(tài),這樣就減少了判斷條件的產(chǎn)生。
電梯類圖
?
代碼設(shè)計(jì)
Liftstate 類:
package Statement;public abstract class LiftState {protected Context context;public abstract void open();public abstract void close();public abstract void run();public abstract void stop();public Context getContext() {return context;}public void setContext(Context context) {this.context = context;} }OpeningState
package Statement;public class OpeningState extends LiftState{@Overridepublic void open() {System.out.println("電梯開(kāi)門了!");}@Overridepublic void close() {context.setCurstate(Context.close);context.getCurstate().close();}@Overridepublic void run() {}@Overridepublic void stop() {} }Closingstate
package Statement;import javafx.scene.paint.Stop;public class ClosingState extends LiftState{@Overridepublic void open() {context.setCurstate(Context.open);context.open();}@Overridepublic void close() {System.out.println("電梯關(guān)門了!");}@Overridepublic void run() {context.setCurstate(Context.run);context.run();}@Overridepublic void stop() {context.setCurstate(Context.stop);context.stop();} }RunningState
package Statement;public class RunningState extends LiftState {@Overridepublic void open() {}@Overridepublic void close() {}@Overridepublic void run() {System.out.println("電梯開(kāi)始運(yùn)行了!");}@Overridepublic void stop() {context.setCurstate(Context.stop);context.getCurstate().stop();} }StoppingState
package Statement;public class StoppingState extends LiftState {@Overridepublic void open() {context.setCurstate(Context.open);context.getCurstate().open();}@Overridepublic void close() {}@Overridepublic void run() {context.setCurstate(Context.run);context.getCurstate().run();}@Overridepublic void stop() {System.out.println("電梯停止了!");} }Context環(huán)境角色類:?
package Statement;public class Context {public final static OpeningState open=new OpeningState();public static final ClosingState close=new ClosingState();public static final RunningState run=new RunningState();public static final StoppingState stop=new StoppingState();private LiftState curstate;public LiftState getCurstate() {return curstate;}public void setCurstate(LiftState curstate) {this.curstate = curstate;this.curstate.setContext(this);}public void open() {curstate.open();}public void close() {curstate.close();}public void run() {curstate.run();}public void stop() {curstate.stop();} }Client測(cè)試類?
package Statement;public class Client {public static void main(String[] args) {Context con=new Context();con.setCurstate(Context.close);con.open();con.close();con.run();con.stop();} }效果圖:
?
狀態(tài)模式優(yōu)點(diǎn):
1. 結(jié)構(gòu)清晰,避免可判斷語(yǔ)句的使用,避免了程序的復(fù)雜性,提高了可維護(hù)性。
2.遵循設(shè)計(jì)原則,體現(xiàn)了開(kāi)閉原則與單一職責(zé)原則。
3.封裝性很好,這是狀態(tài)模式的基本要求,狀態(tài)變換放置在類的內(nèi)部來(lái)實(shí)現(xiàn),外部的調(diào)用不知道如何實(shí)現(xiàn)狀態(tài)與行為的變換。
缺點(diǎn):
若一個(gè)事物有多個(gè)狀態(tài),就會(huì)導(dǎo)致子類太多了,產(chǎn)生類膨脹。
適用場(chǎng)景
1. 行為隨狀態(tài)而改變的場(chǎng)景?
這是狀態(tài)模式的根本出發(fā)點(diǎn),例如權(quán)限設(shè)計(jì),比如視頻網(wǎng)站的普通會(huì)員,Vip,超級(jí)Vip,權(quán)限不同,能看的東西就不同。
2.條件,分支判斷語(yǔ)句的替換者
程序中有大量判斷語(yǔ)句,?可以通過(guò)擴(kuò)展子類來(lái)實(shí)現(xiàn)條件的判斷處理。?
匆匆寫完,有問(wèn)題歡迎指正!
總結(jié)
 
                            
                        - 上一篇: 资金盘网络传销形式
- 下一篇: linux live下载地址,Clone
