摘要:我們以登錄場(chǎng)景設(shè)計(jì)一個(gè)狀態(tài)機(jī)。第三步,定義狀態(tài)機(jī)配置,設(shè)置初始狀態(tài),以及狀態(tài)與事件之間的關(guān)系。
原文地址:梁桂釗的博客博客地址:http://blog.720ui.com/
狀態(tài)機(jī)中,每個(gè)狀態(tài)有著相應(yīng)的行為,隨著行為的觸發(fā)來(lái)切換狀態(tài)。其中一種做法是使用二維數(shù)組實(shí)現(xiàn)狀態(tài)機(jī)機(jī)制,其中橫坐標(biāo)表示行為,縱坐標(biāo)表示狀態(tài),具體的數(shù)值則表示當(dāng)前的狀態(tài)。
我們以登錄場(chǎng)景設(shè)計(jì)一個(gè)狀態(tài)機(jī)。
這時(shí),我們?cè)O(shè)計(jì)一張狀態(tài)機(jī)表。
那么,此時(shí)它的二維數(shù)組,如下所示。
此外,我們也可以通過(guò)狀態(tài)模式實(shí)現(xiàn)一個(gè)狀態(tài)機(jī)。狀態(tài)模式將每一個(gè)狀態(tài)封裝成獨(dú)立的類,具體行為會(huì)隨著內(nèi)部狀態(tài)而改變。狀態(tài)模式用類表示狀態(tài),這樣我們就能通過(guò)切換類來(lái)方便地改變對(duì)象的狀態(tài),避免了冗長(zhǎng)的條件分支語(yǔ)句,讓系統(tǒng)具有更好的靈活性和可擴(kuò)展性。
現(xiàn)在,我們定義一個(gè)狀態(tài)枚舉,其中包括未連接、已連接、注冊(cè)中、已注冊(cè) 4 種狀態(tài)。
public enum StateEnum { // 未連接 UNCONNECT(1, "UNCONNECT"), // 已連接 CONNECT(2, "CONNECT"), // 注冊(cè)中 REGISTING(3, "REGISTING"), // 已注冊(cè) REGISTED(4, "REGISTED"); private int key; private String value; StateEnum(int key, String value) { this.key = key; this.value = value; } public int getKey() {return key;} public String getValue() {return value;} }
定義一個(gè)環(huán)境類,它是實(shí)際上是真正擁有狀態(tài)的對(duì)象。
public class Context { private State state; public void connect(){ state.connect(this); System.out.println("STATE : " + state.getCurState()); } public void register(){ state.register(this); System.out.println("STATE : " + state.getCurState()); } public void registerSuccess(){ state.registerSuccess(this); System.out.println("STATE : " + state.getCurState()); } public void registerFailed(){ state.registerFailed(this); System.out.println("STATE : " + state.getCurState()); } public void unRegister(){ state.unRegister(this); System.out.println("STATE : " + state.getCurState()); } public State getState() { return state; } public void setState(State state) { this.state = state; } }
狀態(tài)模式用類表示狀態(tài),這樣我們就能通過(guò)切換類來(lái)方便地改變對(duì)象的狀態(tài)?,F(xiàn)在,我們定義幾個(gè)狀態(tài)類。
public interface State { void connect(Context c); void register(Context c); void registerSuccess(Context c); void registerFailed(Context c); void unRegister(Context c); String getCurState(); } public class UnconnectState implements State { @Override public void connect(Context c) { c.setState(new ConnectState()); } @Override public void register(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void registerSuccess(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void registerFailed(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void unRegister(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public String getCurState() { return StateEnum.UNCONNECT.toString(); } } public class ConnectState implements State { @Override public void connect(Context c) { c.setState(new ConnectState()); } @Override public void register(Context c) { c.setState(new RegistingState()); } @Override public void registerSuccess(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void registerFailed(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void unRegister(Context c) { c.setState(new UnconnectState()); } @Override public String getCurState() { return StateEnum.CONNECT.toString(); } } public class RegistingState implements State { @Override public void connect(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void register(Context c) { c.setState(new RegistingState()); } @Override public void registerSuccess(Context c) { c.setState(new RegistedState()); } @Override public void registerFailed(Context c) { c.setState(new UnconnectState()); } @Override public void unRegister(Context c) { c.setState(new UnconnectState()); } @Override public String getCurState() { return StateEnum.REGISTING.toString(); } } public class RegistedState implements State { @Override public void connect(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void register(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void registerSuccess(Context c) { c.setState(new RegistedState()); } @Override public void registerFailed(Context c) { throw new RuntimeException("INVALID_OPERATE_ERROR"); } @Override public void unRegister(Context c) { c.setState(new UnconnectState()); } @Override public String getCurState() { return StateEnum.REGISTED.toString(); } }
注意的是,如果某個(gè)行為不會(huì)觸發(fā)狀態(tài)的變化,我們可以拋出一個(gè) RuntimeException 異常。此外,調(diào)用時(shí),通過(guò)環(huán)境類控制狀態(tài)的切換,如下所示。
public class Client { public static void main(String[] args) { Context context = new Context(); context.connect(); context.register(); } }
Spring StateMachine 讓狀態(tài)機(jī)結(jié)構(gòu)更加層次化,可以幫助開發(fā)者簡(jiǎn)化狀態(tài)機(jī)的開發(fā)過(guò)程?,F(xiàn)在,我們來(lái)用 Spring StateMachine 進(jìn)行改造。修改 pom 文件,添加 Maven 依賴。
org.springframework.statemachine spring-statemachine-core 1.2.0.RELEASE
定義一個(gè)狀態(tài)枚舉,其中包括未連接、已連接、注冊(cè)中、已注冊(cè) 4 種狀態(tài)。
public enum RegStatusEnum { // 未連接 UNCONNECTED, // 已連接 CONNECTED, // 注冊(cè)中 REGISTERING, // 已注冊(cè) REGISTERED; }
定義一個(gè)行為枚舉,其中包括連接、注冊(cè)、注冊(cè)成功、注冊(cè)失敗、注銷 5 種行為事件。
public enum RegEventEnum { // 連接 CONNECT, // 注冊(cè) REGISTER, // 注冊(cè)成功 REGISTER_SUCCESS, // 注冊(cè)失敗 REGISTER_FAILED, // 注銷 UN_REGISTER; }
接著,我們需要進(jìn)行狀態(tài)機(jī)配置,其中 @EnableStateMachine 注解,標(biāo)識(shí)啟用 Spring StateMachine 狀態(tài)機(jī)功能。
@Configuration @EnableStateMachine public class StateMachineConfig extends EnumStateMachineConfigurerAdapter{ }
我們需要初始化狀態(tài)機(jī)的狀態(tài)。其中,initial(RegStatusEnum.UNCONNECTED) 定義了初始狀態(tài)是未連接狀態(tài)。states(EnumSet.allOf(RegStatusEnum.class)) 定義了狀態(tài)機(jī)中存在的所有狀態(tài)。
@Override public void configure(StateMachineStateConfigurerstates) throws Exception { states.withStates() // 定義初始狀態(tài) .initial(RegStatusEnum.UNCONNECTED) // 定義狀態(tài)機(jī)狀態(tài) .states(EnumSet.allOf(RegStatusEnum.class)); }
我們需要初始化當(dāng)前狀態(tài)機(jī)有哪些狀態(tài)事件。其中, source 指定原始狀態(tài),target 指定目標(biāo)狀態(tài),event 指定觸發(fā)事件。
@Override public void configure(StateMachineTransitionConfigurertransitions) throws Exception { // 1.連接事件 // 未連接 -> 已連接 .withExternal() .source(RegStatusEnum.UNCONNECTED) .target(RegStatusEnum.CONNECTED) .event(RegEventEnum.CONNECT) .and() .withExternal() .source(RegStatusEnum.CONNECTED) .target(RegStatusEnum.CONNECTED) .event(RegEventEnum.CONNECT) .and() // 2.注冊(cè)事件 // 已連接 -> 注冊(cè)中 .withExternal() .source(RegStatusEnum.CONNECTED) .target(RegStatusEnum.REGISTERING) .event(RegEventEnum.REGISTER) .and() .withExternal() .source(RegStatusEnum.REGISTERING) .target(RegStatusEnum.REGISTERING) .event(RegEventEnum.REGISTER) .and() // 3.注冊(cè)成功事件 // 注冊(cè)中 -> 已注冊(cè) .withExternal() .source(RegStatusEnum.REGISTERING) .target(RegStatusEnum.REGISTERED) .event(RegEventEnum.REGISTER_SUCCESS) .and() .withExternal() .source(RegStatusEnum.REGISTERED) .target(RegStatusEnum.REGISTERED) .event(RegEventEnum.REGISTER_SUCCESS) .and() // 4.注冊(cè)失敗事件 // 注冊(cè)中 -> 未連接 .withExternal() .source(RegStatusEnum.REGISTERING) .target(RegStatusEnum.UNCONNECTED) .event(RegEventEnum.REGISTER_FAILED) .and() // 5.注銷事件 // 已連接 -> 未連接 .withExternal() .source(RegStatusEnum.CONNECTED) .target(RegStatusEnum.UNCONNECTED) .event(RegEventEnum.UN_REGISTER) .and() // 注冊(cè)中 -> 未連接 .withExternal() .source(RegStatusEnum.REGISTERING) .target(RegStatusEnum.UNCONNECTED) .event(RegEventEnum.UN_REGISTER) .and() // 已注冊(cè) -> 未連接 .withExternal() .source(RegStatusEnum.REGISTERED) .target(RegStatusEnum.UNCONNECTED) .event(RegEventEnum.UN_REGISTER) ; }
Spring StateMachine 提供了注解配置實(shí)現(xiàn)方式,所有 StateMachineListener 接口中定義的事件都能通過(guò)注解的方式來(lái)進(jìn)行配置實(shí)現(xiàn)。這里以連接事件為案例,@OnTransition 中 source 指定原始狀態(tài),target 指定目標(biāo)狀態(tài),當(dāng)事件觸發(fā)時(shí)將會(huì)被監(jiān)聽到從而調(diào)用 connect() 方法。
@WithStateMachine public class StateMachineEventConfig { @OnTransition(source = "UNCONNECTED", target = "CONNECTED") public void connect() { System.out.println("http:///////////////////"); System.out.println("連接事件, 未連接 -> 已連接"); System.out.println("http:///////////////////"); } @OnTransition(source = "CONNECTED", target = "REGISTERING") public void register() { System.out.println("http:///////////////////"); System.out.println("注冊(cè)事件, 已連接 -> 注冊(cè)中"); System.out.println("http:///////////////////"); } @OnTransition(source = "REGISTERING", target = "REGISTERED") public void registerSuccess() { System.out.println("http:///////////////////"); System.out.println("注冊(cè)成功事件, 注冊(cè)中 -> 已注冊(cè)"); System.out.println("http:///////////////////"); } @OnTransition(source = "REGISTERED", target = "UNCONNECTED") public void unRegister() { System.out.println("http:///////////////////"); System.out.println("注銷事件, 已注冊(cè) -> 未連接"); System.out.println("http:///////////////////"); } }
Spring StateMachine 讓狀態(tài)機(jī)結(jié)構(gòu)更加層次化,我們來(lái)回顧下幾個(gè)核心步驟:第一步,定義狀態(tài)枚舉。第二步,定義事件枚舉。第三步,定義狀態(tài)機(jī)配置,設(shè)置初始狀態(tài),以及狀態(tài)與事件之間的關(guān)系。第四步,定義狀態(tài)監(jiān)聽器,當(dāng)狀態(tài)變更時(shí),觸發(fā)方法。
(完)
更多精彩文章,盡在「服務(wù)端思維」微信公眾號(hào)!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68216.html
摘要:以下內(nèi)容來(lái)自我特別喜歡的一個(gè)頻道這是一個(gè)年你成為前端,后端或全棧開發(fā)者的進(jìn)階指南你不需要學(xué)習(xí)所有的技術(shù)成為一個(gè)開發(fā)者這個(gè)指南只是通過(guò)簡(jiǎn)單分類列出了技術(shù)選項(xiàng)我將從我的經(jīng)驗(yàn)和參考中給出建議首選我們會(huì)介紹通用的知識(shí)最后介紹年的的一些趨勢(shì)基礎(chǔ)前端開 以下內(nèi)容來(lái)自我特別喜歡的一個(gè)Youtube頻道: Traversy Media 這是一個(gè)2019年你成為前端,后端或全棧開發(fā)者的進(jìn)階指南: 你...
摘要:分布式架構(gòu)實(shí)踐負(fù)載均衡在網(wǎng)站創(chuàng)立初期,我們一般都使用單臺(tái)機(jī)器對(duì)臺(tái)提供集中式服務(wù),但是隨著業(yè)務(wù)量越來(lái)越大,無(wú)論是性能上還是穩(wěn)定性上都有了更大的挑戰(zhàn)。就鹿晗宣布戀情導(dǎo)致微博宕機(jī)事件淺談大型網(wǎng)站高可用性架構(gòu)中午吃飯刷著刷著微博發(fā)現(xiàn)微博突然掛了。 分布式架構(gòu)實(shí)踐——負(fù)載均衡 在網(wǎng)站創(chuàng)立初期,我們一般都使用單臺(tái)機(jī)器對(duì)臺(tái)提供集中式服務(wù),但是隨著業(yè)務(wù)量越來(lái)越大,無(wú)論是性能上還是穩(wěn)定性上都有了更大的挑...
摘要:入坑指南是滴,下面是一個(gè)最低的入坑還應(yīng)該有種設(shè)計(jì)模式應(yīng)該掌握的。堆棧以幀為單位保存線程的狀態(tài),對(duì)堆棧的操作為壓棧和出棧執(zhí)行字節(jié)碼以后,將會(huì)產(chǎn)生程序計(jì)數(shù)器和棧,程序計(jì)數(shù)器存放將要執(zhí)行下一條指令的偏移量。 Java入坑指南是滴,下面是一個(gè)最低的入坑 還應(yīng)該有23種設(shè)計(jì)模式應(yīng)該掌握的。╮(╯▽╰)╭注意,第一個(gè)j是大寫。 Java的特點(diǎn)跨平臺(tái),風(fēng)格接近C++最重要的api文檔 https:/...
摘要:后端好書閱讀與推薦系列文章后端好書閱讀與推薦后端好書閱讀與推薦續(xù)后端好書閱讀與推薦續(xù)二后端好書閱讀與推薦續(xù)三后端好書閱讀與推薦續(xù)四這里依然記錄一下每本書的亮點(diǎn)與自己讀書心得和體會(huì),分享并求拍磚。 后端好書閱讀與推薦系列文章:后端好書閱讀與推薦后端好書閱讀與推薦(續(xù))后端好書閱讀與推薦(續(xù)二)后端好書閱讀與推薦(續(xù)三)后端好書閱讀與推薦(續(xù)四) 這里依然記錄一下每本書的亮點(diǎn)與自己讀書心得...
摘要:在實(shí)際的企業(yè)開發(fā)中,不可能所有情況都是從頭到尾的按狀態(tài)流程來(lái),會(huì)有很多意外,比如歷史數(shù)據(jù),故障重啟后的遺留流程,所以這種可以任意調(diào)節(jié)狀態(tài)的才是我們需要的狀態(tài)機(jī)。 1、偽持久化和中間段的狀態(tài)機(jī)我們?cè)O(shè)想一個(gè)業(yè)務(wù)場(chǎng)景,就比如訂單吧,我們一般的設(shè)計(jì)都會(huì)把訂單狀態(tài)存到訂單表里面,其他的業(yè)務(wù)信息也都有表保存,而狀態(tài)機(jī)的主要作用其實(shí)是規(guī)范整個(gè)訂單業(yè)務(wù)流程的狀態(tài)和事件,所以狀態(tài)機(jī)要不要保存真的不重要,...
閱讀 2808·2021-10-09 09:44
閱讀 3578·2019-08-30 15:54
閱讀 2194·2019-08-30 14:16
閱讀 2817·2019-08-30 13:09
閱讀 852·2019-08-30 13:08
閱讀 1308·2019-08-29 16:29
閱讀 1710·2019-08-26 13:57
閱讀 1955·2019-08-26 13:53