<tbody id="86a2i"></tbody>


<dd id="86a2i"></dd>
<progress id="86a2i"><track id="86a2i"></track></progress>

<dd id="86a2i"></dd>
<em id="86a2i"><ruby id="86a2i"><u id="86a2i"></u></ruby></em>

    <dd id="86a2i"></dd>
    Jcloud

    作者:京東物流 鄭朋輝

    1 簡介

    Squirrel狀態機是一種用來進行對象行為建模的工具,主要描述對象在它的生命周期內所經歷的狀態,以及如何響應來自外界的各種事件。比如訂單的創建、已支付、發貨、收獲、取消等等狀態、狀態之間的控制、觸發事件的監聽,可以用該框架進行清晰的管理實現。使用狀態機來管理對象生命流的好處更多體現在代碼的可維護性、可測試性上,明確的狀態條件、原子的響應動作、事件驅動遷移目標狀態,對于流程復雜易變的業務場景能大大減輕維護和測試的難度。

    2 基本概念

    2.1 Squirrel狀態機定義

    Squirrel狀態機是一種有限狀態機,有限狀態機是指對象有一個明確并且復雜的生命流(一般而言三個以上狀態),并且在狀態變遷存在不同的觸發條件以及處理行為。

    2.2 Squirrel狀態機要素

    Squirrel狀態機可歸納為4個要素,即現態、條件、動作、次態?!艾F態”和“條件”是因,“動作”和“次態”是果。

    • 現態:是指當前所處的狀態。
    • 條件:又稱為事件。當一個條件被滿足,將會觸發一個動作,或者執行一次狀態的遷移。
    • 動作:條件滿足后執行的動作。動作執行完畢后,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足后,也可以不執行任何動作,直接遷移到新狀態。
    • 次態:條件滿足后要遷往的新狀態?!按螒B”是相對于“現態”而言的,“次態”一旦被激活,就轉變成新的“現態”了。

    3 實現原理

    3.1 店鋪審核CASE

    舉例,京東線上開店需要經過審核才能正式上線,店鋪狀態有待審核、已駁回、已審核,對應操作有提交審核,審核通過,審核駁回動作?,F在需要實現一個店鋪審核流程的需求。

    3.2 方案對比

    3.2.1 常用if-else或switch-case實現(分支模式)

    圖1.if-else/switch-case模式實現流程圖

    3.2.2 狀態機實現

    圖2.狀態機模式實現流程圖

    3.2.3 對比

    通過引入狀態機,可以去除大量if-else if-else或者switch-case分支結構,直接通過當前狀態和狀態驅動表查詢行為驅動表,找到具體行為執行操作,有利于代碼的維護和擴展。

    3.3 實現原理

    圖3.狀態機創建流程圖

    • StateMachine: StateMachine實例由StateMachineBuilder創建不被共享,對于使用annotation方式(或fluent api)定義的StateMachine,StateMachine實例即根據此定義創建,相應的action也由本實例執行,與spring的集成最終要的就是講spring的bean實例注入給由builder創建的狀態機實例;
    • StateMachineBuilder: 本質上是由StateMachineBuilderFactory創建的動態代理。被代理的StateMachineBuilder默認實現為StateMachineBuilderImpl,內部描述了狀態機實例創建細節包括State、Event、Context類型信息、constructor等,同時也包含了StateMachine的一些全局共享資源包括StateConverter、EventConverter、MvelScriptManager等。StateMachineBuilder可被復用,使用中可被實現為singleton;
    • StateMachineBuilderFactory: 為StateMachineBuilder創建的動態代理實例;

    4 實踐分享

    4.1 環境依賴

    <dependency>
    <groupId>org.squirrelframework</groupId>
    <artifactId>squirrel-foundation</artifactId>
    <version>0.3.9</version>
    </dependency>
    

    4.2 狀態機元素定義:狀態、事件

    // 店鋪審核狀態
    public Enum ShopInfoAuditStatusEnum{
    audit(0,"待審核"),
    agree(1,"審核通過"),
    reject(2,"審核駁回");
    }
    // 店鋪審核事件
    public Enum ShopInfoAuditEvent{
    SUBMIT, // 提交
    AGREE, // 同意
    REJECT; // 駁回
    }
    

    4.3 構建StateMachineBuilder實例

    /**
    * StateMachineBuilder實例
    */
    public class StateMachineEngine <T extends UntypedStateMachine, S, E, C> implements ApplicationContextAware{
    
    
    private ApplicationContext applicationContext;
    
    
    private static Map<String,UntypedStateMachineBuilder> builderMap = new HashMap<String,UntypedStateMachineBuilder>();
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
    }
    
    
    @Transactional
    public void fire(Class<T> machine, S state, E event, C context) {
    StateMachineBuilder stateMachineBuilder = this.getStateMachineBuilder(machine);
    StateMachine stateMachine = stateMachineBuilder.newStateMachine(state,applicationContext);
    stateMachine.fire(event, context);
    }
    
    
    private StateMachineBuilder getStateMachineBuilder(Class<T> stateMachine){
    UntypedStateMachineBuilder stateMachineBuilder = builderMap.get(stateMachine.getName());
    if(stateMachineBuilder == null){
    stateMachineBuilder = StateMachineBuilderFactory.create(stateMachine,ApplicationContext.class);
    builderMap.put(stateMachine.getName(),stateMachineBuilder);
    }
    return stateMachineBuilder;
    

    4.4 創建具體店鋪狀態審核狀態機

    /**
    * 店鋪審核狀態機
    */
    @States({
    @State(name = "audit"),
    @State(name = "agree"),
    @State(name = "reject")
    })
    @Transitions({
    @Transit(from = "audit", to = "agree", on = "AGREE", callMethod = "agree"),
    @Transit(from = "audit", to = "reject", on = "REJECT", callMethod = "reject"),
    @Transit(from = "reject", to = "audit", on = "SUBMIT", callMethod = "submit"),
    @Transit(from = "agree", to = "audit", on = "SUBMIT", callMethod = "submit"),
    @Transit(from = "audit", to = "audit", on = "SUBMIT", callMethod = "submit"),
    })
    @StateMachineParameters(stateType=ShopInfoAuditStatusEnum.class, eventType=ShopInfoAuditEvent.class, contextType=ShopInfoAuditStatusUpdateParam.class)
    public class ShopInfoAuditStateMachine extends AbstractUntypedStateMachine {
    
    
    private ApplicationContext applicationContext;
    
    
    public ShopInfoAuditStateMachine(){}
    
    
    public ShopInfoAuditStateMachine(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
    }
    
    
    // 審核通過業務邏輯
    public void agree(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
    this.agree(fromState,toState,event,param);
    }
    
    // 審核駁回業務邏輯
    public void reject(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
    this.reject(fromState,toState,event,param);
    }
    
    // 提交業務邏輯
    public void submit(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
    this.submit(fromState,toState,event,param);
    }
    

    4.5 客戶端調用

    // 調用端
    main{
    StateMachineEngine stateMachineEngine = applicationContext.getBean(StateMachineEngine.class);
    // 審核通過調case
    stateMachineEngine.fire(ShopInfoAuditStateMachine.class,ShopInfoAuditStatusEnum.audit,ShopInfoAuditEvent.AGREE,param);
    // 審核駁回case
    stateMachineEngine.fire(ShopInfoAuditStateMachine.class,ShopInfoAuditStatusEnum.audit,ShopInfoAuditEvent.REJECT,param);
    }
    

    5 總結

    狀態機很好的幫我們處理了對象狀態的流轉、事件的監聽以及外界的各種事件的響應。從代碼設計角度減少了大量if-else/switch-case邏輯判斷,提高了代碼的可維護性、擴展性,方便管理和測試。

    相關文章:

    免费一级a片在线播放视频|亚洲娇小性XXXX色|曰本无码毛片道毛片视频清|亚洲一级a片视频免费观看
    <tbody id="86a2i"></tbody>

    
    
    <dd id="86a2i"></dd>
    <progress id="86a2i"><track id="86a2i"></track></progress>

    <dd id="86a2i"></dd>
    <em id="86a2i"><ruby id="86a2i"><u id="86a2i"></u></ruby></em>

      <dd id="86a2i"></dd>