後台重構 基於狀態機的事件機制

2021-09-12 03:34:31 字數 4758 閱讀 2899

對於後台業務實現方式的重構,源於目前的業務場景太多太複雜,我們寫**的時候總不免落入「偽物件導向」的方式,以瀑布流的形式一直在原來的業務**上疊新的**,建立不同的分支。導致每提乙個小需求,就要在各個地方去增加**,而且還會遺漏,並且**的質量難以維護。

出於這樣的困境,我們想將現在實現的業務場景進行高度抽象和歸類,很多業務**,無非是對資料狀態的變更。比如一條資料a,在業務場景a下,只是從狀態a翻轉成狀態b。而在場景b下,除了翻轉狀態,還會去發一條簡訊。

場景a:資料a(狀態a -> 狀態b)

場景b:資料a(1. 狀態a -> 狀態b  2. 發簡訊)

所以這裡完全用狀態機的機制去實現狀態翻轉的工作,另外再處理額外的功能(比如場景b的發簡訊)。

狀態機的理解是:資料a遇到事件1,會從源狀態翻轉到目標狀態 

資料a: 源狀態 --(事件1)--> 目標狀態
1. 狀態機表

我們用了一張狀態機表來儲存這種變化關係:

create table `task_status_trans_rule` (

`id` bigint(64) not null auto_increment comment 'id',

`task_type` varchar(25) default null comment '任務型別',

`src_status` varchar(15) default null comment '源狀態',

`dest_status` varchar(15) default null comment '目標狀態',

`event_type_name` varchar(32) default null comment '事件型別',

`remark` varchar(50) default null comment '描述',

`create_time` datetime default null comment '建立時間',

`modify_time` datetime default null comment '修改時間',

primary key (`id`)

) engine=innodb auto_increment=1 default charset=utf8mb4 comment='任務狀態轉換規則表';

id

task_type

src_status

dest_status

event_type_name

remark

create_time

modify_time

1task_1

status_a

status_b

event_a

任務1-狀態a-狀態b-事件a

2019-05-10 09:00:00

2019-05-10 09:00:00

2task_2

status_c

status_d

event_b

任務2-狀態c-狀態d-事件b

2019-05-10 09:00:00

2019-05-10 09:00:00

其中,一條資料就會有一種對應的任務型別,某種事件型別會將其狀態進行翻轉。在這裡我們可以看到,普通的狀態翻轉都是在**上寫死的,不能靈活修改(修改狀態需要在下一版本發版解決),而且需要找到**中所有需要修改的地方進行修改。

update table set status = 'close' where id = 1;
用狀態機表來維護資料狀態翻轉狀態的好處,是不需要修改**(開放閉合),且統一管理。我們專案將所有業務場景的狀態翻轉維護在這張表裡,也僅僅有200多行資料。

2. 狀態機的繼承

狀態機表裡的狀態翻轉資料雖說只有幾百條,但這些跟業務強相關的基礎資料盡量要保持不變,因為一旦修改,很難馬上從肉眼上判斷出這次修改對業務邏輯的具體影響,需要很好的回歸測試。另外,對於狀態機表資料本身,其實也隱含了「子類繼承父類」的關係。

比如:id

task_type

src_status

dest_status

event_type_name

remark

create_time

modify_time

1task

status_a

status_b

event_a

任務-狀態a-狀態b-事件a

2019-05-10 09:00:00

2019-05-10 09:00:00

2task_1

status_a

status_b

event_a

任務1-狀態a-狀態b-事件a

2019-05-10 09:00:00

2019-05-10 09:00:00

3task_2

status_a

status_b

event_a

任務2-狀態a-狀態b-事件a

2019-05-10 09:00:00

2019-05-10 09:00:00

4task_3

status_a

status_c

event_a

任務3-狀態a-狀態c-事件a

2019-05-10 09:00:00

2019-05-10 09:00:00

從表中可看出,對於同乙個事件event_a,且資料的源狀態都是status_a的前提下,task、task_1和task_2都是翻轉成status_b,而task_3是翻轉成status_c。

task是父類,task_1、task_2、task3都是task的子類,這種父子類的關係,是通過「相同字首」(都有相同的字首:task)來實現的。由於task_1和task_2的狀態機和其父類task的狀態機一致,所以可以用繼承父類的方式來減少配置的狀態機資料。而task_3的狀態機和task的狀態機不一致,就是保持子類的特殊性

故上表可改造成:

idtask_type

src_status

dest_status

event_type_name

remark

create_time

modify_time

1task

status_a

status_b

event_a

任務-狀態a-狀態b-事件a

2019-05-10 09:00:00

2019-05-10 09:00:00

2task_3

status_a

status_c

event_a

任務3-狀態a-狀態c-事件a

2019-05-10 09:00:00

2019-05-10 09:00:00

表中已經不再重複定義task_1和task_2的狀態機,那在**上如何獲取到task_1的狀態機呢?

對狀態表進行查詢,查詢條件是task_type, src_status, event_type_name,查詢結果是dest_status

首先用task_1進行查詢:

select dest_status from task_status_trans_rule 

where task_type = 'task_1' and src_status = 'status_a' and event_type_name = 'event_a';

如果此查詢有結果,說明有子類特別的狀態機。(此時task_1肯定沒結果,因為表中沒有task_1的狀態機。而查詢task_3是有結果的,為status_c)

如果沒有結果,則將task_1改為其父類task,再次查詢:

select dest_status from task_status_trans_rule 

where task_type = 'task' and src_status = 'status_a' and event_type_name = 'event_a';

其結果是status_b,則實現了狀態機的繼承,這可大大減少狀態機資料的配置量。

3. 狀態機的實現

由前面可知,很多業務場景都只涉及到狀態翻轉,只有一些複雜場景會有額外的操作,比如發簡訊。

故我們定義乙個狀態機抽象類abstracttaskeventlistener,先從狀態機表獲取到對應事件的資料,執行統一的翻轉狀態方法trueto(),額外的操作在具體的子類中通過重寫父類的抽象方法processevent()來實現。

public abstract class abstracttaskeventlistener 

}protected void processevent(task task, event event)

}

當業務場景還涉及額外的業務邏輯,就可定義乙個子類,在processevent寫具體的業務邏輯。

@component

public class subtaskeventlistener extends abstracetaskeventlistener

}

基於狀態機的按鍵程式

基於狀態機的按鍵程式 一般的按鍵輸入軟體介面程式非常簡單,在程式中一旦檢測到按鍵輸入口為低電平 有時可能為高 便採用軟體延時的方法來進行消抖,然後再次檢測按鍵輸入,如果再次確認為低電平則表示有按鍵按下,轉入執行按鍵處理程式。如果延時後檢測的電平為高電平則放棄本次按鍵檢測,重新開始一次按鍵檢測過程。在...

基於狀態機的遊戲框架

有限狀態機就是乙個具有有限數量狀態,並且能夠根據相應的操作從乙個狀態變換到另乙個狀態,而在同一時刻只能處在一種狀態下的智慧型體。英文 finite state machine 簡稱 fsm 最簡單的狀態機 if else 實際上if else就是乙個最有兩種狀態的狀態機,分別是true和false ...

基於狀態機的簡單控制

基於狀態機的簡單控制 公司是做一些簡單的夾具的,大部分功能是幾個按鍵加上幾個電磁閥,再加乙個顯示屏就構成了乙個簡單的控制系統。工控行業一般要求是比較穩定的,所以我需要在啟動電磁閥之後去判斷我的氣缸是否到了指定位置,這樣我們就需要用到了sensor,那麼問題來了 首先我按下了按鍵,氣缸也動作了,接下來...