之所以是要做狀態(tài)機痊乾,是因為最近工作上的業(yè)務成分實在侵入性太多施逾,代碼可以搞定碧磅,但是不易維護旗闽,更不夠優(yōu)雅。比如
一個搜索框蜡塌,對應四個選項卡類型搜索碉纳,牽扯到4個或更多的服務調(diào)用,拿到數(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))
簡單介紹下生命周期
/*
組件創(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ù)轉調(diào)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)機匹配(二)