干掉項(xiàng)目中雜亂的 if-else,試試狀態(tài)模式,這才是優(yōu)雅的實(shí)現(xiàn)方式!
?
IF-ELSE 方式
原來以為寫一個(gè)簡單的類型翻譯器花不了太多時(shí)間,可是真做起來,才發(fā)現(xiàn)要注意的點(diǎn)太多了。
首先是處理容器的開啟和閉合,這就需要使用棧來保存預(yù)期的下一個(gè)字符類型,再對(duì)比棧頂字符類型和當(dāng)前處理字符,決定解析的結(jié)果。
還要注意類型嵌套的情況下,內(nèi)層嵌套的容器作為外層容器的元素被解析完成時(shí),需要修改外層容器的預(yù)期字符。而且 Map 作為一種相對(duì) Set 和 List 比較特殊的容器,還要處理它的左右元素。同時(shí)還不能忘記處理各種異常,如未知字符、容器內(nèi)是原始類型、容器未正確閉合等。
而這些邏輯混雜在一塊就更添復(fù)雜度了,通常是一遍代碼寫下來挺順暢,找?guī)讉€(gè)特殊的 case 一驗(yàn)證,往往就有沒有考慮到的點(diǎn),你以為解決了這個(gè)點(diǎn)就好了,殊不知這個(gè)問題點(diǎn)的解決方案又引起了另一個(gè)問題。
最終修修補(bǔ)補(bǔ)好多次,終于把代碼寫完了,連優(yōu)化的想法都沒了,擔(dān)心又引入新的問題。更多 Java 核心技術(shù)教程:https://github.com/javastacks/javastack,一起來學(xué)習(xí)吧。
最終的偽代碼如下:
public?String?parseToFullType()?throws?IllegalStateException?{
????StringBuilder?sb?=?new?StringBuilder();
????for?(;?;?this.scanner.next())?{
????????Character?currentChar?=?scanner.current();
????????if?(currentChar?==?'\uFFFF')?{
????????????return?sb.toString();
????????}
????????if?(isCollection())?{
????????????if?(CollectionEnd())?{
????????????????dealCollectionEleEnd();
????????????}else?{
????????????????throw?new?IllegalStateException("unexpected?char?'"?+?currentChar?+?"'?at?position?"?+?scanner.getIndex());
????????????}
????????}?else?if?(isWrapperType())?{
????????????dealSingleEleEnd();
????????}?else?if?(parseStart())?{
????????????if?(collectionStart())?{
????????????????putCollecitonExpectEle()
????????????}
????????}?else?{
????????????throw?new?IllegalStateException("unknown?char?'"?+?currentChar?+?"'?at?position?"?+?scanner.getIndex());
????????}
????}
?
狀態(tài)機(jī)方式
狀態(tài)機(jī)。狀態(tài)機(jī)
有限狀態(tài)機(jī)(finite-state machine,縮寫:FSM)又稱有限狀態(tài)自動(dòng)機(jī)(finite-state automation,縮寫:FSA),簡稱狀態(tài)機(jī),是表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動(dòng)作等行為的數(shù)學(xué)計(jì)算模型。
狀態(tài) State:狀態(tài)是一個(gè)系統(tǒng)在其生命周期的某一刻時(shí)的運(yùn)行狀態(tài),如駕車的例子中狀態(tài)就包括 正常速度行駛、停車和低速行駛?cè)N狀態(tài)。 事件 Event:事件就是某一時(shí)刻施加于系統(tǒng)的某個(gè)信號(hào),在上面的例子中事件是指紅燈、綠燈和黃燈。所有的狀態(tài)變化都要依賴事件,但事件也可能導(dǎo)致狀態(tài)不發(fā)生變化,如正常行駛中遇到綠燈就不用做什么反應(yīng)。 變換 Transition:變換是在事件發(fā)生之后系統(tǒng)要做出的狀態(tài)變化,如上面例子中的減速、停車或加速。 動(dòng)作 Action:動(dòng)作是同樣是事件發(fā)生之后系統(tǒng)做出的反應(yīng),不同的是,動(dòng)作不會(huì)改變系統(tǒng)狀態(tài),像駕車遇到紅燈停車后,喝水這個(gè)動(dòng)作沒有對(duì)系統(tǒng)狀態(tài)造成影響。
狀態(tài)拆分

首先是確定狀態(tài),我定義了 Start/SetStart/SetEle/ListStart/ListEel/MapStart/MapLeft/MapRight 八種基礎(chǔ)狀態(tài),由于一次只解析一個(gè)類型,容器閉合就代表著解析結(jié)束,所以沒有對(duì)各個(gè)容器設(shè)置結(jié)束狀態(tài)。又因?yàn)橛袪顟B(tài)嵌套的存在,而一個(gè)狀態(tài)沒法表達(dá)狀態(tài)機(jī)的準(zhǔn)確狀態(tài),需要使用棧來存儲(chǔ)整體的解析狀態(tài),我使用這個(gè)棧為空來代表 End 狀態(tài),又省略了一個(gè)狀態(tài)。
再拆分事件,事件是掃描到的每一個(gè)字符,由于字符種類較多,而像 integer 和 double、String 和 Long 的處理又沒有什么區(qū)別,我將事件類型抽象為 包裝類型元素(WRAPPED_ELE),原始類型元素(PRIMITIVE_ELE),MAP、List 和 Set 五種。
變幻和動(dòng)作都是事件發(fā)生后系統(tǒng)的反應(yīng),在我的需要里需要轉(zhuǎn)變解析狀態(tài),并將結(jié)構(gòu)結(jié)果保存起來。這里我將它們整體抽象為一個(gè)事件處理器接口,如:
public?interface?StateHandler?{
????/**
?????*?@param?event?要處理的事件
?????*?@param?states?系統(tǒng)整體狀態(tài)
?????*?@param?result?解析的結(jié)果
?????*/
????void?handle(Event?event,?Stack?states,?StringBuilder?result);
}
代碼示例
public?class?MapLeftHandler?implements?StateHandler?{
????@Override
????public?void?handle(Event?event,?Stack?states,?StringBuilder?result)?{
????????//?這里是核心的?Action,將單步解析結(jié)果放到最終結(jié)果內(nèi)
????????result.append(",");
????????result.append(event.getParsedVal());
????????//?狀態(tài)機(jī)的典型處理方式,處理各種事件發(fā)生在當(dāng)前狀態(tài)時(shí)的邏輯
????????switch?(event.getEventType())?{
????????????case?MAP:
????????????????states.push(State.MAP_START);
????????????????break;
????????????case?SET:
????????????????states.push(State.SET_START);
????????????????break;
????????????case?LIST:
????????????????states.push(State.LIST_START);
????????????????break;
????????????case?WRAPPED_ELE:
????????????????//?使用?pop?或?push?修改棧頂狀態(tài)來修改解析器的整體狀態(tài)
????????????????states.pop();
????????????????states.push(State.MAP_RIGHT);
????????????????break;
????????????case?PRIMITIVE_ELE:
????????????????//?當(dāng)前狀態(tài)不能接受的事件類型要拋異常中斷
????????????????throw?new?IllegalStateException("unexpected?primitive?char?'"?+?event.getCharacter()?+?"'?at?position?"?+?event.getIndex());
????????????default:
????????}
????}
}
public?static?String?parseToFullType(String?shortenType)?throws?IllegalStateException?{
????StringBuilder?result?=?new?StringBuilder();
????StringCharacterIterator?scanner?=?new?StringCharacterIterator(shortenType);
????Stack?states?=?new?Stack<>();
????states.push(State.START);
????for?(;?;?scanner.next())?{
????????char?currentChar?=?scanner.current();
????????if?(currentChar?==?'\uFFFF')?{
????????????return?result.toString();
????????}
????????//?使用整體狀態(tài)為空來代表解析結(jié)束
????????if?(states.isEmpty())?{
????????????throw?new?IllegalStateException("unexpected?char?'"?+?currentChar?+?"'?at?position?"?+?scanner.getIndex());
????????}
????????//?將字符規(guī)整成事件對(duì)象,有利于參數(shù)的傳遞
????????Event?event?=?Event.parseToEvent(currentChar,?scanner.getIndex());
????????if?(event?==?null)?{
????????????throw?new?IllegalStateException("unknown?char?'"?+?currentChar?+?"'?at?position?"?+?scanner.getIndex());
????????}
????????//?這里需要一個(gè)?Map?來映射狀態(tài)和狀態(tài)處理器
????????STATE_TO_HANDLER_MAPPING.get(states.peek()).handle(event,?states,?result);
????}
}
