狀態(tài)機匹配(一)(從零實現(xiàn))

之所以是要做狀態(tài)機,是因為最近工作上的業(yè)務成分實在侵入性太多,代碼可以搞定,但是不易維護,更不夠優(yōu)雅。比如
一個搜索框,對應四個選項卡類型搜索,牽扯到4個或更多的服務調用,拿到數(shù)據(jù)還需去重聚合,提取想要的數(shù)據(jù)后,還需要本地數(shù)據(jù)庫匹配組裝,等等。

比如這是控制器,我需要分發(fā)出去,部分外部服務還不支持分頁

 /*
    顧客APP查詢接口
     */
    @MethodInfo(code = "CustomerControllerQuery")
    @GetMapping("/query")
    String customersQuery(
            @PageableDefault Pageable pageable,
            Principal principal,
            String keyword,
            @RequestParam(defaultValue = "0") int tabIndex
    ){
        String userName = principal.getName();

        switch (tabIndex){
            case 0:
                return handlePhoneNumber(keyword,userName,pageable);
            case 1:
                return handleGoods(keyword,userName);
            case 2:
                return handlePreStoreOrder(keyword,userName);
            case 3:
                return handleDisposition(keyword,userName,pageable);
        }


        return JSON.toJSONString(ResultDTO.builder()
                .message("操作失敗")
                .isSuccess(false)
                .build());
    }

比如從第二個選項卡篩選顧客關聯(lián)的一些信息

String handlePreStoreOrder(String keyword,String userName){
       val  prestoreOrderReq = IOrderServiceClient
                                       .PrestoreOrderReq
                                       .builder()
                                       .employeeCode(userName)
                                       .orderName(keyword)
                                       .build();
       ResultDTO<List<IOrderServiceClient.PrestoreOrderResp>>
               resultDTO = iOrderServiceClient.findOrderList(
                       iOrderServiceClient.beanToMap(prestoreOrderReq)
                           );
       if (resultDTO.getIsSuccess()){
           val data=resultDTO.getData().stream()
                   .filter(c -> c.getCustomerId() != null).collect(Collectors.toList());
           Set<IOrderServiceClient.PrestoreOrderResp> customerSet=
                   new TreeSet<>((o1, o2) -> o1.getCustomerId().compareTo(o2.getCustomerId()));
           customerSet.addAll(data);
           if (customerSet != null && customerSet.size() > 0) {
               val customerTuples=customerSet.parallelStream()
                       .map(
                               c -> {
                                   List tasks=taskRepository.findAllByUserNameAndCustomerId(
                                           1,
                                           userName, c.getCustomerId()
                                   );
                                   return CustomerTuple.builder()
                                           .id(c.getCustomerId())
                                           .customerName(c.getCustomerName())
                                           .customerMobile(c.getCustomerPhone())
                                           .existTask((tasks == null || tasks.size() == 0) ? false : true)
                                           .tasks(
                                                   tasks
                                           )
                                           .build();
                               }

                       ).collect(Collectors.toList());

               return JSON.toJSONString(ResultDTO.builder()
                       .data(buildPageInfo(customerTuples))
                       .build(), new SimplePropertyPreFilter() {
                   {
                       getExcludes().add("taskDetails");
                   }
               }, WriteMapNullValue);
           }
       }
       return JSON.toJSONString(ResultDTO.builder().isSuccess(resultDTO.getIsSuccess()).data(buildPageInfo(resultDTO.getData())).build());
   }

業(yè)務無錯,只是架構規(guī)劃,導致實現(xiàn)起來非常不流暢

所以這倆天產(chǎn)生了關于狀態(tài)機的想法,并且初步實現(xiàn)了

需要提供具體的mapFunc容器,為了優(yōu)雅的使用,我也做了一個提取注解的mapFunc,使用者也可以使用如下的實例,就可以產(chǎn)生mapFunc容器

@Slf4j
@Builder
public class DemoFuncMap implements IMapFunction {

    @MapFunctionListener(name = "測試01",group = "t",code = "test01")
    public StateNode test01(StateNode<Map,ResultDTO> currentNode){
        log.info("test01");
        return StateNode.builder().nodeCode("t_test03").build();
    }

    @MapFunctionListener(name = "測試02",group = "t",code = "test02")
    public StateNode test02(StateNode currentNode){
        log.info("test02");
        return StateNode.builder().response(ResultDTO.builder().message("測試終點狀態(tài)").build()).build();
    }

    @MapFunctionListener(name = "測試03",group = "t",code = "test03")
    public StateNode test03(StateNode currentNode){
        log.info("test03");
        //測試異常
        //int ss = 1/0;
        return StateNode.builder().nodeCode("t_test02").build();
    }
}

這是state對應的Func
當然如果你想更靈活或者豐富使用可以傳遞一個

@Slf4j
//@Component
@Data
@Accessors(chain = true)
public abstract class StateComponent
        <T extends StateNode,Resource,
        Container extends Map<T,IMapRouterProcessStateNodeFunc<T>>,
                Response extends ResultDTO,Ex extends Exception> {

Container自組類型就是mapFunc的映射容器
下面我來幾本介紹下StateComponent這個組件吧
一.StateComponent結構
a.他是一個抽象類(為了實現(xiàn)生命周期,參考andorid的Activity的實現(xiàn))


圖片.png

簡單介紹下生命周期

/*
        組件創(chuàng)建
        提供初始化對象
         */
    public abstract T onCreate();

    /*
    提供屬性加載
    提供容器加載
    設置結束節(jié)點
     */
    public abstract Pair<Resource,T> onStart();


    /*
    優(yōu)先使用注解class的mapFunction
     */
    public Container onLoadContainer(IMapFunction<T> iMapFunction,Container injectContainer){
        Container container = injectContainer;
        if (this.iMapFunctionListenerProcess!=null){
            container = (Container) this.iMapFunctionListenerProcess.handler(iMapFunction);
        }
        return container;
    }

b.別名類型 比較不太友好的是java不支持靈活的自組別名類型(swift kotlin就很靈活實現(xiàn)了)

        <T extends StateNode,Resource,
        Container extends Map<T,IMapRouterProcessStateNodeFunc<T>>,
                Response extends ResultDTO,Ex extends Exception>

第一個表示繼承狀態(tài)節(jié)點的類型,第二個Resource表示資源的類型,第三個是存儲mapFunc的容器,第四個是響應,第五個是異常類型
c.狀態(tài)的組裝及循環(huán)執(zhí)行
這個也屬于生命周期內(nèi)部方法,使用可選擇性覆蓋,也可默認執(zhí)行
考慮到狀態(tài)的更替,所以使用狀態(tài)體的循環(huán)知道發(fā)生異?;驁?zhí)行到終止狀態(tài)

/*
    組件執(zhí)行
     */
    public   T onProcess(Container functionContainer){
        T responseNode=null;
        //結束flag
        boolean flag = false;
        while (!flag){
            val loopState = iTranslateState.translate(this);

            flag = loopState.end();

            //到達最后一個狀態(tài)節(jié)點
            //或者發(fā)生異常了的情況,終止
            if (flag){
                //結束所有節(jié)點循環(huán)
                log.info("狀態(tài)循環(huán)機制結束");
                //執(zhí)行最后的終點節(jié)點狀態(tài)
                log.info("執(zhí)行最后的終點節(jié)點狀態(tài)");
                responseNode =  iTranslateState.translate(this).getStateCode();

            }
        }
        return responseNode;
    }

節(jié)點間鏈表式的引用,并且處理傳遞捕獲到的異常,節(jié)點續(xù)傳


  @Override
    public StateComponent<T,Resource,Container,Response,Ex>
    translate(
            StateComponent<T,Resource,Container,Response,Ex> stateComponent
            )
    {
        //判斷當前是否最后一個節(jié)點
        //Boolean endFlag = stateComponent.getStateCode().end(stateComponent.getEndStateCode());
        /*
        最后一個節(jié)點可以設置響應,靈活配置
         */
        //if (endFlag) stateNode.setResponse(ResultDTO.builder().message("最后一個節(jié)點可以設置響應").build());
        val current = stateComponent.getStateCode();
        val next = iComboMapRouterProcessFunc.routerFunc(
                current,
                stateComponent.getContainer(),
                current
        );

        if (next.getEx()!=null){
            stateComponent.setStateCode(stateComponent.getEndStateCode());
            //組裝異常到最后節(jié)點
            stateComponent.getEndStateCode().setEx(next.getEx());
            return stateComponent;
        }

        current.setAfterState(next);
        //否則返回新節(jié)點,并設置上一節(jié)點,前后關聯(lián)
        next.setBeforeState(current);
        stateComponent.setStateCode(
                (T) next
        );



        return stateComponent;
    }

d.注解式的mapFunc定義
這里我在狀態(tài)3做了一個測試異常,以便于做狀態(tài)處理及傳遞的異常處理,
異常處理,分了倆處捕獲
a.mapFunc容器里接口類型對接反射的函數(shù)引用的異常捕獲并設置到節(jié)點內(nèi)部去

//函數(shù)轉調method,帶同代理注解方法
                            IMapRouterProcessStateNodeFunc<StateNode>
                                    iMapRouterProcessStateNodeFunc =
                                    currentNode -> {
                                        StateNode newStateNode = null;
                                        try {
                                            //newStateNode = (StateNode) method.invoke(clazz,currentNode);
                                            newStateNode = (StateNode) method.invoke(existMapFunction,currentNode);
                                        } catch (IllegalAccessException e) {
                                            e.printStackTrace();
                                            currentNode.setEx(e);
                                        } catch (InvocationTargetException e) {
                                            e.printStackTrace();
                                            currentNode.setEx(e);
                                        }finally {
                                            if (newStateNode==null){
                                                newStateNode = currentNode;

                                            }
                                        }
                                        return newStateNode;
                                    };

b.除了a所術,StateNode節(jié)點本身的定義也做了異常的處理機制
他實現(xiàn)了IStateException這個異常代理,所以在狀態(tài)機的執(zhí)行就可以很方便的補貨異常信息,至于a的情況,是因為反射本身的異常比較特殊,所以單獨處理,這樣便于區(qū)分異常情況(業(yè)務導致還是代碼本身結構異常)

public class StateNode<RequestTuple,Response extends ResultDTO> implements IExactFilter<StateNode>,IStateEndFace<StateNode>,IStateNodeFlow,IStateException

以下就是一個簡單的狀態(tài)的循環(huán)的機遇注解的例子demo

@Slf4j
@Builder
public class DemoFuncMap implements IMapFunction {

    @MapFunctionListener(name = "測試01",group = "t",code = "test01")
    public StateNode test01(StateNode<Map,ResultDTO> currentNode){
        log.info("test01");
        return StateNode.builder().nodeCode("t_test03").build();
    }

    @MapFunctionListener(name = "測試02",group = "t",code = "test02")
    public StateNode test02(StateNode currentNode){
        log.info("test02");
        return StateNode.builder().response(ResultDTO.builder().message("測試終點狀態(tài)").build()).build();
    }

    @MapFunctionListener(name = "測試03",group = "t",code = "test03")
    public StateNode test03(StateNode currentNode){
        log.info("test03");
        //測試異常
        int ss = 1/0;
        return StateNode.builder().nodeCode("t_test02").build();
    }
}

結尾:
希望有時間寫到狀態(tài)機匹配(二)

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容